题意:有一棵树,每条边有一个距离,求dis(u,v)<=k的点的对数
题解:树分治,对于一颗树上的两点,要么在同一颗子树上,要么在不同子树上,要么一个点是根,另一个在某一子树上,对于第一种情况我们可以通过递归来变成第二种或者第三种情况。我们对于某一颗子树来说我们先统计dis[u]+dis[v]<=k的点的对数,然后把该子树的所有子节点为根的这颗子树中dis[u]+dis[v]<=k的点的对数删去,因为在递归到后面会计算重复(这是第一种情况),所以这样就得到了该子树的满足条件的点对数,那么剩下就是如何分了,要想使分的次数最小,那么我们可以每次选取该子树的重心来作为根节点,然后,每次计算完之后删除根节点,再计算所有子树,计算重心还是通过树形dp来求解,
对于树分治来说,分为点分治和边分治,点分治就是每次找到重心,然后把重心删除掉,对分成的树之间统计路径信息,直到只剩一个点的情况
#include<map> #include<set> #include<cmath> #include<queue> #include<stack> #include<vector> #include<cstdio> #include<cassert> #include<iomanip> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair<int,int> #define C 0.5772156649 #define pi acos(-1.0) #define ll long long #define mod 1000000007 #define ls l,m,rt<<1 #define rs m+1,r,rt<<1|1 using namespace std; using namespace __gnu_cxx; const double g=10.0,eps=1e-7; const int N=10000+10,maxn=300000+10,inf=0x3f3f3f; struct edge{ int to,Next,c; }e[maxn]; int cnt,head[N]; int sz[N],zx[N],dis[N]; int n,k; bool vis[N]; void add(int u,int v,int c) { e[cnt].to=v; e[cnt].c=c; e[cnt].Next=head[u]; head[u]=cnt++; } void init() { cnt=0; memset(head,-1,sizeof head); memset(vis,0,sizeof vis); } void dfssz(int u,int f,int &sum) { sum++; sz[u]=1; for(int i=head[u];~i;i=e[i].Next) { int x=e[i].to; if(x==f||vis[x])continue; dfssz(x,u,sum); sz[u]+=sz[x]; } } void dfszx(int u,int f,int sum,int &ans,int &id) { zx[u]=sum-sz[u]; for(int i=head[u];~i;i=e[i].Next) { int x=e[i].to; if(x==f||vis[x])continue; dfszx(x,u,sum,ans,id); zx[u]=max(zx[u],sz[x]); } if(ans>zx[u]) { ans=zx[u]; id=u; } } int findzx(int root)//找子树的重心 { int sum=0; dfssz(root,-1,sum); int ans=inf,id=0; dfszx(root,-1,sum,ans,id); return id; } void dfsdis(int u,int f,vector<int> &son) { for(int i=head[u];~i;i=e[i].Next) { int x=e[i].to; if(x==f||vis[x])continue; dis[x]=dis[u]+e[i].c; dfsdis(x,u,son); } son.pb(dis[u]); } bool comp(int a,int b) { return dis[a]<dis[b]; } int ok(vector<int> &son) { sort(son.begin(),son.end()); // for(int i=0;i<son.size();i++) // cout<<son[i]<<endl; // cout<<endl; int ans=0; for(int i=0;i<son.size();i++) { int p=upper_bound(son.begin(),son.end(),k-son[i])-son.begin(); if(p>0)p--; if(p<=i)continue; // cout<<p<<endl; ans+=p-i; } // cout<<ans<<"*****"<<endl; return ans; } void dfsson(int u,int f,vector<int> &son) { son.pb(dis[u]); for(int i=head[u];~i;i=e[i].Next) { int x=e[i].to; if(x==f||vis[x])continue; dfsson(x,u,son); } } int solve(int root) { int p=findzx(root);//找该子树的重心 memset(dis,-1,sizeof dis); dis[p]=0; vector<int>son; son.clear(); dfsdis(p,-1,son);//求子树中各点到重心的距离 int ans=ok(son);//满足该子树的dis[x]+dis[y]<=k对数; // if(root==3)cout<<ans<<" ----"<<endl; for(int i=head[p];~i;i=e[i].Next) { int x=e[i].to; if(vis[x])continue; son.clear(); dfsson(x,p,son); ans-=ok(son); } // if(root==3)cout<<ans<<" ****"<<endl; vis[p]=1; for(int i=head[p];~i;i=e[i].Next) { int x=e[i].to; if(vis[x])continue; ans+=solve(e[i].to); //if(x==3)cout<<solve(e[i].to)<<"......."<<endl; } // if(root==3)cout<<p<<" "<<ans<<"+++++++++"<<endl; return ans; } int main() { ios::sync_with_stdio(false); cin.tie(0); while(~scanf("%d%d",&n,&k)) { if(!n&&!k)break; init(); for(int i=1;i<n;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } printf("%d ",solve(1)); } return 0; } /******************* 4 3 1 2 3 1 3 1 1 4 2 ********************/