点分治总结

点分治总结

开坑。

做几道例题之后填。

噫噫噫没例题做

好像都出动态点分治去了(Orz)

下面是分割线!

上面是分割线!

点分治

瞎BB一个:点分治适用于处理树上路径的统计问题。

eg. 问你一棵树上距离为k的点对数量。

思想

一棵有根树上的路径只有两种:过根的和不过根的。

如果是过根的,可以(O(n))解决。

处理完过根的路径之后,这个树根没有用了,所以删掉。树被分成了若干个联通块,递归下去处理即可。

期望(O(nlog_2n))

实现步骤

思想都有了你还不会实现吗辣鸡

  1. 选一个树根,将无根树转化为有根树。
    如果随便选根可能会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联通块中选根
  1. 统计这个联通块过根的路径。
    以上面的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]);
	}
  1. 删除根节点。
    vis[rt]=0;
  1. 递归下去,处理子树。

几道例题

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;
}

没了。。我去学动态点分治了。。。

原文地址:https://www.cnblogs.com/xzz_233/p/8290131.html