点分治我感觉是图论树部分比较考验脑力的一种题目了
POJ1741
题意:给一棵边带权树,问两点之间的距离小于等于K的点对有多少个
满足条件的点对有两种情况:两个点的路径横跨树根,两个点位于同一颗子树中
对于根节点进行一次dfs,求出deep,并将其从小到大排序
void getdeep(int x,int fa) { dep[++dep[0]]=d[x]; for(int tmp=g[x];tmp;tmp=e[tmp].next) { if(e[tmp].t==fa||vis[e[tmp].t]) continue; d[e[tmp].t]=d[x]+e[tmp].v; getdeep(e[tmp].t,x); } }
然后看一下calculate
int cal(int x,int tmp) { d[x]=tmp;dep[0]=0; getdeep(x,0); sort(dep+1,dep+dep[0]+1); int t=0,l,r; for(l=1,r=dep[0];l<r;) { if(dep[l]+dep[r]<=k) {t+=r-l;l++;} else r--; } return t; }
如果我们已经知道了此时所有点到根的距离a[i]
a[x] + a[y] <= k的(x, y)对数就是结果,这个可以通过排序之后O(n)的复杂度求出
然后根据分治的思想,分别对所有的儿子求一遍即可
void work(int x) //点分治 { ans+=cal(x,0); vis[x]=1; for(int tmp=g[x];tmp;tmp=e[tmp].next) { if(vis[e[tmp].t]) continue; ans-=cal(e[tmp].t,e[tmp].v); sum=ch[e[tmp].t]; root=0; getroot(e[tmp].t,root); work(root); } }
但是这会出现重复的——当前情况下两个点位于一颗子树中,那么应该将其减掉
显然这两个点是满足题意的,为什么减掉呢?因为在对子树进行求解的时候,会重新计算
在进行分治时,为了避免树退化成一条链而导致时间复杂度变为O(N^2),每次都找树的重心
这样,所有的子树规模就会变的很小了。时间复杂度O(Nlog^2N)
void getroot(int x,int fa) { ch[x]=1;f[x]=0; for(int tmp=g[x];tmp;tmp=e[tmp].next) { if(e[tmp].t==fa||vis[e[tmp].t]) continue; getroot(e[tmp].t,x); ch[x]+=ch[e[tmp].t]; f[x]=max(f[x],ch[e[tmp].t]); } f[x]=max(f[x],sum-ch[x]); if(f[x]<f[root]) root=x; }
有了板子不能救命,还要大量刷大量刷才可以,点分治确实是一个要命的地方,上次的省赛的一道题就栽了
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int maxn=10005; 6 const int maxm=20005; 7 const int INF=0x7fffffff; 8 int n,k,ans; 9 int cnt,g[maxn]; 10 struct Edge{int t,next,v;}e[maxm]; 11 12 int root,sum; 13 int ch[maxn],f[maxn],dep[maxn],d[maxn]; 14 bool vis[maxn]; 15 16 void addedge(int u,int v,int w) 17 { 18 cnt++; 19 e[cnt].t=v;e[cnt].v=w; 20 e[cnt].next=g[u];g[u]=cnt; 21 } 22 int read() 23 { 24 int x=0,f=1;char ch=getchar(); 25 while(ch<'0'||ch>'9') {if(ch=='0')f=-1;ch=getchar();} 26 while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();} 27 return x*f; 28 } 29 void getroot(int x,int fa) 30 { 31 ch[x]=1;f[x]=0; 32 for(int tmp=g[x];tmp;tmp=e[tmp].next) 33 { 34 if(e[tmp].t==fa||vis[e[tmp].t]) continue; 35 getroot(e[tmp].t,x); 36 ch[x]+=ch[e[tmp].t]; 37 f[x]=max(f[x],ch[e[tmp].t]); 38 } 39 f[x]=max(f[x],sum-ch[x]); 40 if(f[x]<f[root]) root=x; 41 } 42 void getdeep(int x,int fa) 43 { 44 dep[++dep[0]]=d[x]; 45 for(int tmp=g[x];tmp;tmp=e[tmp].next) 46 { 47 if(e[tmp].t==fa||vis[e[tmp].t]) continue; 48 d[e[tmp].t]=d[x]+e[tmp].v; 49 getdeep(e[tmp].t,x); 50 } 51 } 52 int cal(int x,int tmp) 53 { 54 d[x]=tmp;dep[0]=0; 55 getdeep(x,0); 56 sort(dep+1,dep+dep[0]+1); 57 int t=0,l,r; 58 for(l=1,r=dep[0];l<r;) 59 { 60 if(dep[l]+dep[r]<=k) {t+=r-l;l++;} 61 else r--; 62 } 63 return t; 64 } 65 void work(int x) //点分治 66 { 67 ans+=cal(x,0); 68 vis[x]=1; 69 for(int tmp=g[x];tmp;tmp=e[tmp].next) 70 { 71 if(vis[e[tmp].t]) continue; 72 ans-=cal(e[tmp].t,e[tmp].v); 73 sum=ch[e[tmp].t]; 74 root=0; 75 getroot(e[tmp].t,root); 76 work(root); 77 } 78 } 79 void init() 80 { 81 ans=root=cnt=0; 82 memset(vis,0,sizeof(vis)); 83 memset(g,0,sizeof(g)); 84 } 85 int main() 86 { 87 int x,y,z; 88 while(1) 89 { 90 init(); 91 n=read();k=read(); 92 if(n==0) break; 93 for(int i=1;i<n;i++) 94 { 95 x=read();y=read();z=read(); 96 addedge(x,y,z); 97 addedge(y,x,z); 98 } 99 sum=n; 100 f[0]=INF; 101 getroot(1,0); 102 work(root); 103 printf("%d ",ans); 104 } 105 106 return 0; 107 }