首页 > 代码库 > 树上的点对 题解

树上的点对 题解

【题意】

给定一棵边带权的无根树,求树上距离不超过k的无序互异点对的个数。

【解法】

树分治练手题……

三种分治,点分治,边分治,链分治,都可以。

点分治应该也不难写,然而懒得写平衡树了,就用的相对好想好写的边分治。(因为感觉点分治需要用平衡树……)

点分治是选重心当根节点,然后统计经过根节点的路径数。边分治则是直接找一个切断后能使两边的树尽量平衡的边,统计经过边的路径数。

具体写的时候把两边的距离分别扔到两个vector里,然后二分即可。或者直接根据单调性线性扫描。

统计路径的时候如果线性扫描的话,那么复杂度是O(nlogn),好像比点分治的O(nlog2n)快一些。然而由于常数限制,实际上还是很慢的。

最坏情况下递归深度会达到O(n)(比如菊花形的树),所以要通过插入白点的方式改进最坏复杂度。通过在点到儿子的路径上插入白点来把每个点的儿子个数降到<=2,这样每个点的度数均不超过3,递归的最大深度为O(logn)。

由于建成的树形似线段树,所以树的规模最多扩大一倍,只是常数问题。总复杂度还是O(nlogn)。

第一次写树分治,写的比较丑……凑合看吧。

技术分享
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<queue>
  6 using namespace std;
  7 const int maxn=50010;
  8 struct edge{
  9     int to,w,prev;
 10     bool vis;
 11 }e[maxn<<1];
 12 void predfs(int,int);//预处理,添白点
 13 void addedge(int,int,int);
 14 void solve(int);
 15 void dfs(int,int);//算size
 16 void dfs1(int,int,int&);//找用来分治的边
 17 void dfs2(int,int,int,vector<int>&);//算距离
 18 int n,k,last[maxn],len,size[maxn],siz,tmp,ans,du[maxn],x,y,z;
 19 bool white[maxn]={false};
 20 vector<int>a,b;
 21 vector<pair<int,int> >G[maxn];
 22 int main(){
 23 #define MINE
 24 #ifdef MINE
 25     freopen("poj1741_tree.in","r",stdin);
 26     freopen("poj1741_tree.out","w",stdout);
 27 #endif
 28     while(scanf("%d%d",&n,&k)==2&&n&&k){
 29         memset(last,-1,sizeof(last));
 30         memset(white,0,sizeof(white));
 31         memset(du,0,sizeof(du));
 32         for(int i=1;i<=20000;i++)G[i].clear();
 33         len=0;
 34         for(int i=1;i<n;i++){
 35             scanf("%d%d%d",&x,&y,&z);
 36             G[x].push_back(make_pair(y,z));
 37             G[y].push_back(make_pair(x,z));
 38         }
 39         ans=0;
 40         predfs(1,0);
 41         solve(1);
 42         printf("%d\n",ans);
 43     }
 44 #ifndef MINE
 45     printf("\n--------------------DONE--------------------\n");
 46     for(;;);
 47 #endif
 48     return 0;
 49 }
 50 void predfs(int x,int p){
 51     int cnt=G[x].size();
 52     for(int i=0;i<cnt;i++)if(G[x][i].first!=p)predfs(G[x][i].first,x);
 53     queue<pair<int,int> >q;
 54     for(int i=0;i<cnt;i++)if(G[x][i].first!=p)q.push(G[x][i]);
 55     cnt=q.size();
 56     while(cnt>2){
 57         pair<int,int>a=q.front();q.pop();
 58         pair<int,int>b=q.front();q.pop();
 59         int y=++n;
 60         white[y]=true;
 61         addedge(y,a.first,a.second);
 62         addedge(a.first,y,a.second);
 63         addedge(y,b.first,b.second);
 64         addedge(b.first,y,b.second);
 65         q.push(make_pair(y,0));
 66         cnt--;
 67     }
 68     while(!q.empty()){
 69         addedge(x,q.front().first,q.front().second);
 70         addedge(q.front().first,x,q.front().second);
 71         q.pop();
 72     }
 73 }
 74 void addedge(int x,int y,int z){
 75     e[len].to=y;
 76     e[len].w=z;
 77     e[len].prev=last[x];
 78     last[x]=len++;
 79 }
 80 void solve(int x){
 81     tmp=~(1<<31);
 82     int id=-1;
 83     dfs(x,0);
 84     siz=size[x];
 85     dfs1(x,0,id);
 86     if(id==-1)return;
 87     e[id].vis=e[id^1].vis=true;
 88     solve(e[id].to);
 89     solve(e[id^1].to);
 90     a.clear();
 91     b.clear();
 92     dfs2(e[id].to,0,0,a);
 93     dfs2(e[id^1].to,0,0,b);
 94     sort(a.begin(),a.end());
 95     sort(b.begin(),b.end());
 96     int cnta=a.size(),cntb=b.size();
 97     for(int i=0,j=cntb-1;i<cnta;i++){
 98         while(~j&&k-a[i]-e[id].w<b[j])j--;
 99         ans+=j+1;
100     }
101     e[id].vis=e[id^1].vis=false;
102 }
103 void dfs(int x,int p){
104     size[x]=1;
105     for(int i=last[x];i!=-1;i=e[i].prev)if(e[i].to!=p&&!e[i].vis){
106         dfs(e[i].to,x);
107         size[x]+=size[e[i].to];
108     }
109 }
110 void dfs1(int x,int p,int &id){
111     for(int i=last[x];i!=-1;i=e[i].prev)if(e[i].to!=p&&!e[i].vis){
112         if(abs(size[e[i].to]-(siz-size[e[i].to]))<tmp){
113             id=i;
114             tmp=abs(size[e[i].to]-(siz-size[e[i].to]));
115         }
116         dfs1(e[i].to,x,id);
117     }
118 }
119 void dfs2(int x,int p,int d,vector<int>&a){
120     if(!white[x])a.push_back(d);
121     for(int i=last[x];i!=-1;i=e[i].prev)if(e[i].to!=p&&!e[i].vis)dfs2(e[i].to,x,d+e[i].w,a);
122 }
View Code

【后记】

第一道树分治,就这样献给边分治了……

看好多神犇用的都是点分治,感觉自己真奇葩……

 

尽头和开端,总有一个在等你。

树上的点对 题解