点分治我感觉是图论树部分比较考验脑力的一种题目了
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\n",ans);
104 }
105
106 return 0;
107 }
来源:https://www.cnblogs.com/aininot260/p/9461358.html