[poj1741]Tree(点分治+容斥原理)

题意:求树中点对距离<=k的无序点对个数。

解题关键:树上点分治,这个分治并没有传统分治的合并过程,只是分成各个小问题,并将各个小问题的答案相加即可,也就是每层的复杂度并不在合并的过程,是在每层的处理过程。

此题维护的是树上路径,考虑点分治。

点分治的模板题,首先设点x到当前子树跟root的距离为,则满足${d_x} + {d_y} \le k$可以加进答案,但是注意如果x,y在同一棵子树中,就要删去对答案的贡献,因为x,y会在其所在的子树中在计算一次。同一棵子树中不必考虑是否在其同一棵子树中的问题,因为无论是否在他的同一棵子树,都会对他的父节点产生影响。而这些影响都是无意义的。

注意无根树转有根树的过程,需要选取树的重心防止复杂度从$O(n{\log ^2}n)$退化为$O({n^2})$

复杂度:$O(n{\log ^2}n)$

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<cstdlib>
  5 #include<iostream>
  6 #include<cmath>
  7 #define inf 0x3f3f3f3f
  8 #define maxn 10004
  9 using namespace std;
 10 typedef long long ll;
 11 int head[maxn],cnt,n,k,ans,size,s[maxn],f[maxn],root,depth[maxn],num;//vis代表整体的访问情况,每个dfs不应该只用vis来存储
 12 bool vis[maxn];
 13 struct edge{
 14     int to,w,nxt;
 15 }e[maxn<<1];
 16 void add_edge(int u,int v,int w){
 17     e[cnt].to=v;
 18     e[cnt].w=w;
 19     e[cnt].nxt=head[u];
 20     head[u]=cnt++;
 21 }
 22 
 23 inline int read(){
 24     char k=0;char ls;ls=getchar();for(;ls<'0'||ls>'9';k=ls,ls=getchar());
 25     int x=0;for(;ls>='0'&&ls<='9';ls=getchar())x=(x<<3)+(x<<1)+ls-'0';
 26     if(k=='-')x=0-x;return x;
 27 }
 28 
 29 void get_root(int u,int fa){//get_root会用到size
 30     s[u]=1;f[u]=0;//f是dp数组
 31     for(int i=head[u];i!=-1;i=e[i].nxt){
 32         int v=e[i].to;
 33         if(v==fa||vis[v]) continue;
 34         get_root(v,u);
 35         s[u]+=s[v];
 36         f[u]=max(f[u],s[v]);
 37     }
 38     f[u]=max(f[u],size-s[u]);
 39     root=f[root]>f[u]?u:root;
 40 }
 41 
 42 void get_depth_size(int u,int fa,int dis){//同时获取size和depth
 43     depth[num++]=dis;
 44     s[u]=1;
 45     for(int i=head[u];i!=-1;i=e[i].nxt){
 46         int v=e[i].to;
 47         if(v==fa||vis[v]) continue;
 48         get_depth_size(v,u,dis+e[i].w);
 49         s[u]+=s[v];
 50     }
 51 }
 52 
 53 int calc(int u,int fa,int w){
 54     num=0;
 55     get_depth_size(u,fa,w);
 56     sort(depth,depth+num);
 57     int ret=0;
 58     for(int l=0,r=num-1;l<r;){
 59         if(depth[l]+depth[r]<=k) ret+=r-l++;
 60         else r--;
 61     }
 62     return ret;
 63 }
 64 
 65 void work(int u){
 66     vis[u]=true;
 67     ans+=calc(u,-1,0);
 68     for(int i=head[u];i!=-1;i=e[i].nxt){
 69         int v=e[i].to;
 70         if(vis[v]) continue;
 71         ans-=calc(v,u,e[i].w);
 72         size=s[v],root=0;
 73         get_root(v,u);
 74         work(root);
 75     }
 76 }
 77 
 78 void init(){
 79     memset(vis,false, sizeof vis);
 80     memset(head,-1,sizeof head);
 81     ans=cnt=0;
 82 }
 83 
 84 
 85 int main(){
 86     int a,b,c;
 87     f[0]=inf;
 88     while(scanf("%d%d",&n,&k)&&(n||k)){
 89         init();
 90         for(int i=0;i<n-1;i++){
 91             a=read(),b=read(),c=read();
 92             add_edge(a,b,c);
 93             add_edge(b,a,c);
 94         }
 95         size=n,root=0;
 96         get_root(1,-1);
 97         work(root);
 98         printf("%d\n",ans);
 99     }
100     return 0;
101     
102 }

 

posted @ 2017-09-06 17:51  Elpsywk  阅读(247)  评论(0编辑  收藏  举报