原题:POJ-1741
题意:
有一棵n个节点的树,每条边都有一个权值,问有多少个节点之间的距离小于等于k,
解析:
典型的树分治,对于每一棵树,我们首先找到它的重心
重心:一棵树中以这个点为root时的最大子树的节点数最小
int siz[N],maxn[N];//这棵子树大小,最大子树大小
void getG(int p,int fa,int sum){
siz[p]=1;
maxn[p]=0;
for(int i=head[p];~i;i=nex[i]){
if(!vis[to[i]]&&to[i]!=fa){
getG(to[i],p,sum);//这个时候如果重心在下面可以得出G,也可以得出下面的siz
siz[p]+=siz[to[i]];
maxn[p]=max(maxn[p],siz[to[i]]);//找出最大子树
}
}
maxn[p]=max(maxn[p],sum-siz[p]);//当然还要这棵树的上半部分比较
if(maxn[G]>maxn[p])G=p;
}
然后计算和这个节点有关的题目中要求的东西,然后就可以删除这个点,变成很多棵树,在对每棵子树进行同样的操作
其他的就看题目写了
代码:
const int N =10009;
int n,k;
int head[N],nex[2*N],to[2*N],from[2*N],v[2*N],now;
void add(int a,int b,int vv){
to[++now]=b;v[now]=vv;from[now]=a;nex[now]=head[a];head[a]=now;
to[++now]=a;v[now]=vv;from[now]=b;nex[now]=head[b];head[b]=now;
}
int ans;
int G;
int vis[N];//是否删除
int siz[N],maxn[N];//这棵子树大小,最大子树大小
void getG(int p,int fa,int sum){
siz[p]=1;
maxn[p]=0;
for(int i=head[p];~i;i=nex[i]){
if(!vis[to[i]]&&to[i]!=fa){
getG(to[i],p,sum);//这个时候既可以得出G,又可以得出下面的siz
siz[p]+=siz[to[i]];
maxn[p]=max(maxn[p],siz[to[i]]);//找出最大子树
}
}
maxn[p]=max(maxn[p],sum-siz[p]);//当然还要这棵树的上半部分比较
if(maxn[G]>maxn[p])G=p;
}
int dep[N],num,len[N];
void dfs(int p,int fa){
for(int i=head[p];~i;i=nex[i]){
if(vis[to[i]]||fa==to[i])continue;
len[++num]=dep[to[i]]=dep[p]+v[i];
dfs(to[i],p);
}
}
int cal(int p,int length){//重点,多加的部分的消除
int sum=0;
num=0;
len[++num]=dep[p]=length;//自己也加进去
dfs(p,0);//得出离G点距离
sort(len+1,len+1+num);
for(int l=1,r=num;r>l;){
if(dep[r]+dep[l]<=k)sum+=r-l,l++;//r-l对可以匹配,l已经匹配完了
else r--;//r已经不能匹配了
}
return sum;
}
void divide(int p){
//printf("G==%d\n",p);
vis[p]=1;
ans+=cal(p,0);
for(int i=head[p];~i;i=nex[i]){
if(vis[to[i]])continue;
ans-=cal(to[i],v[i]);
G=0;//为了初始化
maxn[0]=siz[to[i]];
getG(to[i],0,siz[to[i]]);//一套做全都是0
divide(G);
}
}
int main(){
while(cin>>n>>k){
if(!n&&!k)break;
ans=0;mmm(head,-1);now=0;mmm(vis,0);
for(int i=1,a,b,v;i<n;i++)a=read(),b=read(),v=read(),add(a,b,v);
maxn[0]=n;//因为在getG的时候可以和maxn[0]比较来初始化G(使第一个fa==0)
G=0;getG(1,0,n);
divide(G);
printf("%d\n",ans);
}
}