点分治总结
开坑。
做几道例题之后填。
噫噫噫没例题做
好像都出动态点分治去了(Orz)
下面是分割线!
上面是分割线!
点分治
瞎BB一个:点分治适用于处理树上路径的统计问题。
eg. 问你一棵树上距离为k的点对数量。
思想
一棵有根树上的路径只有两种:过根的和不过根的。
如果是过根的,可以(O(n))解决。
处理完过根的路径之后,这个树根没有用了,所以删掉。树被分成了若干个联通块,递归下去处理即可。
期望(O(nlog_2n))。
实现步骤
思想都有了你还不会实现吗辣鸡
- 选一个树根,将无根树转化为有根树。
如果随便选根可能会GG,一条链把你卡成(O(n^2))
要保证复杂度,树根必须是树的重心。不证。
重心求法不写。
const int&
这东西好像卡常有点用就用了。
siz[i]
是i子树的大小。
f[i]
是i最大儿子的子树大小。
vis[i]
是删除标记。
il vd getrt(const int&x,const int&FA){
siz[x]=1;f[x]=0;
for(rg int i=fir[x];i;i=nxt[i]){
if(FA==dis[i]||vis[dis[i]])continue;
getrt(dis[i],x);
siz[x]+=siz[dis[i]];
f[x]=max(f[x],siz[dis[i]]);
}
f[x]=max(f[x],sum-siz[x]);
if(f[x]<f[rt])rt=x;
}
il vd Getrt(const int&x,const int&_sum){sum=_sum,f[0]=2e9,rt=0,getrt(x,-1);}
//在包括x的,点数为_sum联通块中选根
- 统计这个联通块过根的路径。
以上面的eg为例。
先处理出所有点到根节点的距离。
il vd getdep(const int&x,const int&FA){
dep[++dep[0]]=dp[x];
for(rg int i=fir[x];i;i=nxt[i]){
if(vis[dis[i]]||dis[i]==FA)continue;
dp[dis[i]]=dp[x]+w[i];
getdep(dis[i],x);
}
}
getdep(x,-1);
然后用尺取法(?)算出深度和<=k的点对数,累加进ans。
这种方法可能会有同一棵子树(没有过根)的点对被计入,所以再去掉那些点对即可。
il int cal(const int&x,const int&d){
dp[x]=d;dep[0]=0;getdep(x,-1);
sort(dep+1,dep+dep[0]+1);
sta int l,r,tot;l=1,r=dep[0],tot=0;
while(l<r)(dep[l]+dep[r]<=k)?(tot+=r-l,++l):--r;
return tot;
}
x=Getrt();
ans+=cal(x,0);
for(rg int i=fir[x];i;i=nxt[i]){
if(vis[dis[i]])continue;
ans-=cal(dis[i],w[i]);
}
- 删除根节点。
vis[rt]=0;
- 递归下去,处理子树。
几道例题
cogs1714(eg):
// It is made by XZZ
#include<cstdio>
#include<algorithm>
#include<cstring>
#define il inline
#define rg register
#define vd void
#define sta static
typedef long long ll;
using namespace std;
il int gi(){
rg int x=0,f=1;rg char ch=getchar();
while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int maxn=10001;
int rt,n,k,fir[maxn],nxt[maxn<<1],dis[maxn<<1],w[maxn<<1],id,ans,f[maxn],siz[maxn],sum,vis[maxn];
int dp[maxn],dep[maxn];
il vd link(int&a,int&b,int&c){nxt[++id]=fir[a],fir[a]=id,dis[id]=b,w[id]=c;}
il vd getrt(const int&x,const int&FA){
siz[x]=1;f[x]=0;
for(rg int i=fir[x];i;i=nxt[i]){
if(vis[dis[i]]||dis[i]==FA)continue;
getrt(dis[i],x);
siz[x]+=siz[dis[i]];
f[x]=max(f[x],siz[dis[i]]);
}
f[x]=max(f[x],sum-siz[x]);
if(f[x]<f[rt])rt=x;
}
il vd getdep(const int&x,const int&FA){
dep[++dep[0]]=dp[x];
for(rg int i=fir[x];i;i=nxt[i]){
if(vis[dis[i]]||dis[i]==FA)continue;
dp[dis[i]]=dp[x]+w[i];
getdep(dis[i],x);
}
}
il int cal(const int&x,const int&d){
dp[x]=d;dep[0]=0;getdep(x,-1);
sort(dep+1,dep+dep[0]+1);
sta int l,r,tot;l=1,r=dep[0],tot=0;
while(l<r)(dep[l]+dep[r]<=k)?(tot+=r-l,++l):--r;
return tot;
}
il vd Getrt(const int&x,const int&_sum){sum=_sum,f[0]=2e9,rt=0,getrt(x,-1);}
il vd solve(const int&x){
ans+=cal(x,0);vis[x]=1;
for(rg int i=fir[x];i;i=nxt[i]){
if(vis[dis[i]])continue;
ans-=cal(dis[i],w[i]);
Getrt(dis[i],siz[dis[i]]);
solve(rt);
}
}
int main(){
int x,y,z;
while(n=gi(),k=gi(),n){
id=0;memset(fir,0,sizeof fir);memset(vis,0,sizeof vis);
for(rg int i=1;i<n;++i)x=gi(),y=gi(),z=gi(),link(x,y,z),link(y,x,z);
Getrt(1,n);
ans=0;
solve(rt);
printf("%d
",ans);
}
return 0;
}
cogs1863 聪聪可可:
这个算出与根距离(mod 3)余数为0,1,2的各有多少个就好辣。
// It is made by XZZ
#include<cstdio>
#include<algorithm>
#define il inline
#define rg register
#define vd void
#define sta static
typedef long long ll;
using namespace std;
il int gi(){
rg int x=0,f=1;rg char ch=getchar();
while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int maxn=20001;
int n,rt,fir[maxn],dis[maxn<<1],nxt[maxn<<1],w[maxn<<1],id,vis[maxn],siz[maxn],f[maxn],sum,dp[maxn];
ll ans,dep[3];
il vd link(const int&x,const int&y,const int&z){nxt[++id]=fir[x],fir[x]=id,dis[id]=y,w[id]=z;}
il vd getrt(const int&x,const int&FA){
siz[x]=1;f[x]=0;
for(rg int i=fir[x];i;i=nxt[i]){
if(FA==dis[i]||vis[dis[i]])continue;
getrt(dis[i],x);
siz[x]+=siz[dis[i]];
f[x]=max(f[x],siz[dis[i]]);
}
f[x]=max(f[x],sum-siz[x]);
if(f[x]<f[rt])rt=x;
}
il vd Getrt(const int&x,const int&_sum){sum=_sum,f[0]=2e9,rt=0,getrt(x,-1);}
il vd getdep(const int&x,const int&FA){
++dep[dp[x]%=3];
for(rg int i=fir[x];i;i=nxt[i]){
if(FA==dis[i]||vis[dis[i]])continue;
dp[dis[i]]=dp[x]+w[i];
getdep(dis[i],x);
}
}
il int cal(const int&x,const int&d){
dp[x]=d;dep[0]=dep[1]=dep[2]=0;getdep(x,-1);
return dep[0]*dep[0]+dep[1]*dep[2]*2;
}
il vd solve(const int&x){
vis[x]=1;ans+=cal(x,0);
for(rg int i=fir[x];i;i=nxt[i]){
if(vis[dis[i]])continue;
Getrt(dis[i],siz[dis[i]]);
ans-=cal(dis[i],w[i]);
solve(rt);
}
}
int main(){
n=gi();
int x,y,z;
for(rg int i=1;i<n;++i)x=gi(),y=gi(),z=gi(),link(x,y,z),link(y,x,z);
Getrt(1,n),solve(rt);
x=ans,y=n*n;while(y)z=y,y=x%y,x=z;
printf("%lld/%lld
",ans/x,(ll)n*n/x);
return 0;
}
没了。。我去学动态点分治了。。。