点分治
点分治的思想:每次选定一个点,然后分别在它的子树内求解①,再计算跨越子树的情况,考虑当前点和每棵子树内的点②以及两两子树内的③。(有点绕)
如图,红代表①,紫代表②,绿代表③。
这样求解的复杂度取决去递归的层数,若树是一条链,那么若每次选择的是链的一个端点,那么一共会有 \(n\) 层,将会超时。
实际上只要选择重心进行分治,就能稳定 \(O(\lg n)\) 层。
为什么呢,因为重心有一个性质:对于重心而言,它任意子节点的子树大小一定不超过自己子树大小 \(n\) 的 \(\dfrac{1}{2}\)。
这可以使用反证法来证明,假设存在这么一个点,那么这个点的子树大小 \(>n\div 2\),则除这部分点外的大小 \(<n\div 2<k\),而这个点的内部就算只有一棵子树,它的子树大小一定 \(\le k-1\)(因为重心要去除自己这个点之后计算),二者均小于最大子树,则那个超标的点就是重心,与已知矛盾。
不理解的可以看图。
所以我们只要让重心作为分治点,那么层数就有保证了。
对于本题,我们考虑答案有三类,就是开头提到的。
对于①,递归即可。
对于②,只需要求出当前点为根其他节点深度,比较一下即可。
对于③,考虑容斥原理,先求出以当前点为根其他节点深度的集合中选择两个数使得深度满足要求的个数(显然这样可能选择两个点在一棵子树中),然后再减去所有以子节点为根的子树的集合(深度值相同)中选择两个数使得深度满足要求的个数,即为所求。
对于在一个数列中选择两个数满足要求,可以先排序,然后使用尺取法即可。
点分治具有最多 \(\lg n\) 层,每层需要排序 \(\lg n\),则复杂度为 \(O(n\lg^2n)\)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define Ed for(int i=h[x];~i;i=ne[i])
#define Ls(i,l,r) for(int i=l;i<r;++i)
#define Rs(i,l,r) for(int i=l;i>r;--i)
#define Le(i,l,r) for(int i=l;i<=r;++i)
#define Re(i,l,r) for(int i=l;i>=r;--i)
#define L(i,l) for(int i=0;i<l;++i)
#define E(i,l) for(int i=1;i<=l;++i)
#define W(t) while(t--)
#define Wh while
const int N=10010,M=2*N;
int h[N],e[M],ne[M],w[M],idx,n,m,p[N],q[N];//don't forget memset h!
bool st[N];
void add(int a,int b,int c){
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dfs_size(int x,int fa){
if(st[x])return 0;
int sum=1;
Ed{
int j=e[i];
if(j==fa)continue;
sum+=dfs_size(j,x);
}
return sum;
}
int find_wc(int x,int fa,int tot,int &u,int &maxn){
if(st[x])return 0;
int mx=0,sum=1;
Ed{
int j=e[i];
if(j==fa)continue;
int tmp=find_wc(j,x,tot,u,maxn);
if(tmp>mx)mx=tmp;
sum+=tmp;
}
if(tot-sum>mx)mx=tot-sum;
if(mx<maxn)u=x,maxn=mx;
return sum;
}
void dfs(int x,int fa,int dep,int &qt){
if(st[x])return;
q[qt++]=dep;
Ed{
int j=e[i];
if(j==fa)continue;
dfs(j,x,dep+w[i],qt);
}
}
int get(int a[],int t){
sort(a,a+t);
int res=0;
for(int i=t-1,j=-1;~i;--i){
while(j+1<i&&a[j+1]+a[i]<=m)++j;
j=j<i-1?j:i-1;
res+=j+1;
}
return res;
}
int calc(int x){
if(st[x])return 0;
int res=0,maxn=1e9;
find_wc(x,-1,dfs_size(x,-1),x,maxn);
st[x]=1;
int pt=0;
Ed{
int j=e[i],qt=0;
dfs(j,-1,w[i],qt);
res-=get(q,qt);
L(k, qt)p[pt++]=q[k],res+=q[k]<=m;
}
res+=get(p,pt);
//part 1
Ed{
int j=e[i];
res+=calc(j);
}
return res;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
#endif
while(scanf("%d%d",&n,&m),n||m){
memset(h,-1,n*4+4);
memset(st,0,n+1);
idx=0;
E(i, n-1){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c),add(b,a,c);
}
printf("%d\n",calc(0));
}
return 0;
}
标签:lg,子树,板题,int,分治,mx,define
From: https://www.cnblogs.com/wscqwq/p/17654569.html