POJ1741 Tree 树分治模板

 
题意:一棵n个点的树,每条边有距离v,求该树中距离小于等于k的点的对数。
 
dis[y]表示点y到根x的距离,v代表根到子树根的距离;
那么不在同一棵子树中的两点i、j之间的距离为dis[i]+dis[j] ① 
 
设得到这个距离的时间复杂度为O(w);
如果我们层层如此递归即可得到所有的点对数量,可以证明复杂度为O(logn*w);
 
因为n的范围为(n<=10000)所以我们需要w与n近似;
 
那么此时问题转化为了如何在大约为O(n)的复杂度内得到①;
一个个计算不在同一子树中显然是麻烦的,如果选择先计算整棵树的点对数量然后去掉重复计数的点对数问题就可以得到简化;
 
如果只是计算一棵树下符合条件dis[j]+dis[i]<=k的点对数量,我们将距离sort,很容易在 log(树的大小) 的复杂度下把问题解决,再用几乎同样的时间减去每一棵子树中符合dis[j]+dis[i]+2*v<=k的点对的数量就可以得到答案。
那么总时间复杂度为O(n*logn*logn);
 
当然这只是理想情况,如果这棵树退化为一条链,复杂度则会变为O(n*n*logn)显然是超时的;
所以在每次递归前O(n)的复杂度找一下树的重心,
 
代码如下
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<cmath>
  6 //using namespace std;
  7 const int maxn=10010;
  8 const double eps=1e-8;
  9 const int modn=45989;
 10 int n,k;
 11 struct nod{
 12     int y,next;
 13     int v;
 14 }e[maxn*2];
 15 int head[maxn]={},siz[maxn]={},ma[maxn]={},dis[maxn]={};
 16 int tot=0,tot1,root=0,now,ans;
 17 bool vis[maxn]={};
 18 void init(int x,int y,int v){
 19     e[++tot].y=y;
 20     e[tot].next=head[x];
 21     e[tot].v=v;
 22     head[x]=tot;
 23 }
 24 void getsiz(int x,int fa){ //某子树的大小
 25     int y;
 26     siz[x]=1;
 27     ma[x]=0;
 28     for(int i=head[x];i;i=e[i].next){
 29         y=e[i].y;
 30         if(y!=fa&&!vis[y]){
 31             getsiz(y,x);
 32             siz[x]+=siz[y];
 33             ma[x]=std::max(siz[y],ma[x]);
 34         }
 35     }
 36 }
 37 void cen(int r,int x,int fa){ //找重心
 38     int y;
 39     if(siz[r]-siz[x]>ma[x]){
 40         ma[x]=siz[r]-siz[x];
 41     }
 42     if(ma[x]<now){
 43         root=x;now=ma[x];
 44     }
 45     for(int i=head[x];i;i=e[i].next){
 46         y=e[i].y;
 47         if(y!=fa&&!vis[y]){
 48             cen(r,y,x);
 49         }
 50     }
 51 }
 52 void getdis(int x,int fa,int di){ //子树中各个点到该子树根的距离
 53     int v,y;
 54     dis[++tot1]=di;
 55     for(int i=head[x];i;i=e[i].next){
 56         v=e[i].v;y=e[i].y;
 57         if(y!=fa&&!vis[y]){
 58             getdis(y,x,di+v);
 59         }
 60     }
 61 }
 62 int sum(int x,int d){ //点对数
 63     tot1=0;
 64     getdis(x,0,d);
 65     std::sort(dis+1,dis+1+tot1);
 66     int i=1,j=tot1;
 67     int cnt=0;
 68     while(i<j){
 69         while(dis[i]+dis[j]>k&&i<j){
 70             j--;
 71         }
 72         cnt+=j-i;
 73         i++;
 74     }
 75     return cnt;
 76 }
 77 void dfs(int x){
 78     int y;
 79     now=maxn;root=x;
 80     getsiz(x,0);
 81     cen(x,x,0);
 82     ans+=sum(root,0);
 83     vis[root]=1;
 84     for(int i=head[root];i;i=e[i].next){
 85         y=e[i].y;
 86         if(!vis[y]){
 87             ans-=sum(y,e[i].v);
 88             dfs(y);
 89         }
 90     }
 91 }
 92 void yu(){ 
 93     memset(head,0,sizeof(head));
 94     memset(vis,0,sizeof(vis));
 95     tot=0;ans=0;
 96 }
 97 int main(){
 98     while((~scanf("%d%d",&n,&k))&&(n!=0||k!=0)){
 99         yu();
100         int x,y,v;
101         for(int i=1;i<n;i++){
102             scanf("%d%d%d",&x,&y,&v);
103             init(x,y,v);
104             init(y,x,v);
105         }
106         dfs(1);
107         printf("%d
",ans);
108     }
109     return 0;
110 }
View Code
原文地址:https://www.cnblogs.com/137shoebills/p/7786503.html