1. 程式人生 > >[poj1741]Tree(點分治+容斥原理)

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

無根樹轉有根樹 while 過程 poj1741 size eof add 處理 num

題意:求樹中點對距離<=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-f[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 }

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