题意:给定一棵树,求树上两点间距离小等于K的所有方案数。
这题怎么想?(我猜一定会有人想lca)
如果这么想,最终一定会顿悟自己陷入了一个复杂度的陷阱。因为这样就是枚举+大暴力。求了LCA还不如不求直接n^2求两点距离呢。。。
那这题我们怎么办。树形dp?其实差不多,就是树形dp,但是,怎么设计是一个问题,稍有不慎就会变成大暴力。
对于根,可以用一个小小的dp求出各点到跟的距离。然后通过排序可以算出符合要求的答案,但是答案肯定不可能仅仅过这一个根节点,那怎么办,我们可以考虑把所有点都当根节点,对于每个根节点求出必经这个根节点的符合题意的路径数。我这里用了必经两个字,显然按照我们之前排序那么求会出现不必经的劣质结果,我们这时就要对结果进行删重,在此可以用图示说明:
这个时候我曲线指明的这两个点显然就是不必经根节点的劣质答案,就要删去,只需要枚举当前根节点的子树,这时就不只是满足小于k的问题了,而是要小于(k-连接子树与根的那条边),这样的结果我们之前肯定记录过,而且不会删去其他不多余的答案。。。
诶我忘了解释为啥删重,只是说了个“显然”,其实挺显然的,再上图:
我们求过答案的“根节点”,就当他不存在了,现在新的根节点,像上面这种情况,就是我们之前求重了的路径,这时才该他起作用呢!
我们不断这样做会出现一个问题,如果树是一条链,那我们就爆n^2了,怎么处理一下,看上图恐怕大家已经发现端倪了,我们每次都找当前剩余树的重心,这样就能保证子树规模相当,从而不被卡。
据说这方法叫树分治。
以上。
1 //昨夜小楼又东风,珠帘泛婆娑湿衣袖 2 #include<iostream> 3 #include<cstdio> 4 #include<algorithm> 5 #include<vector> 6 #include<cstring> 7 using namespace std; 8 const int MAXN=10009; 9 struct node{ 10 int y,z,nxt; 11 }edge[MAXN*2];int head[MAXN];bool vis[MAXN]; 12 int n,k,siz,s[MAXN],f[MAXN],rt,d[MAXN]; 13 vector<int>dep;int indx=0,cnt=0,m,ans; 14 void add(int x,int y,int z){ 15 edge[++indx].y=y;edge[indx].z=z;edge[indx].nxt=head[x];head[x]=indx; 16 } 17 void getroot(int x,int fa){ 18 s[x]=1;f[x]=0; 19 for(int i=head[x],y;~i;i=edge[i].nxt) 20 if((y=edge[i].y)!=fa&&!vis[y]){ 21 getroot(y,x);s[x]+=s[y];f[x]=max(f[x],s[y]); 22 } f[x]=max(f[x],siz-s[x]); 23 if(f[x]<f[rt]) rt=x; 24 } 25 void getdep(int x,int fa){ 26 dep.push_back(d[x]);s[x]=1; 27 for(int i=head[x],y;i!=-1;i=edge[i].nxt){ 28 if((y=edge[i].y)==fa||vis[y]) continue; 29 d[y]=d[x]+edge[i].z;getdep(y,x);s[x]+=s[y]; 30 } 31 } 32 int calc(int x,int init){ 33 dep.clear();d[x]=init;getdep(x,0); 34 sort(dep.begin(),dep.end());int tot=0; 35 for(int l=0,r=dep.size()-1;l<r;) 36 if(dep[l]+dep[r]<=m) tot+=r-l++; 37 else r--; return tot; 38 } 39 void work(int x){ 40 ans+=calc(x,0);vis[x]=true; 41 for(int i=head[x],y;i!=-1;i=edge[i].nxt) 42 if(!vis[y=edge[i].y]){ 43 ans-=calc(y,edge[i].z); 44 f[0]=siz=s[y]; 45 getroot(y,rt=0);work(rt); 46 } 47 } 48 int main(){ 49 while(~scanf("%d%d",&n,&m)&&(n||m)){ 50 memset(head,-1,sizeof(head));indx=0; 51 memset(vis,0,sizeof(vis));dep.clear(); 52 for(int i=1,x,y,z;i<n;i++){ 53 scanf("%d%d%d",&x,&y,&z); 54 add(x,y,z);add(y,x,z); 55 } f[0]=siz=n; 56 getroot(1,rt=0);ans=0; 57 work(rt);printf("%d\n",ans); 58 } return 0; 59 } 60 //恰似故人远来葬花落。