• 点分治学习笔记


    应用

    点分治适合处理大规模的树上路径信息问题。

    个人感觉和 (dsu on tree) 有点像

    过程

    因为是分治所以我们肯定要把一个大问题分解为更小的子问题

    以洛谷上的P4178 Tree为例

    首先我们选择一个点开始递归,求出其它的点到该点的距离,把得到的结果存在一个数组里

    然后把该数组 (sort) 一下,利用双指针求出到该点的距离之和小于等于 (k) 的点对有多少个

    然后把该点打个标记,再去找其它的点进行上述操作

    找什么样的点对我们的复杂度是有很大影响的

    经过证明,每次选择剩余子树的重心是最优的,复杂度为 (nlogn)

    再加上排序的一个 (log) 复杂度为 (nlog^2n)

    要注意的是这种方法需要容斥减去不合法的方案

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    const int maxn=1e6+5;
    int h[maxn],tot=1,n,k;
    struct asd{
    	int to,nxt,val;
    }b[maxn];
    void ad(rg int aa,rg int bb,rg int cc){
    	b[tot].to=bb;
    	b[tot].nxt=h[aa];
    	b[tot].val=cc;
    	h[aa]=tot++;
    }
    int siz[maxn],maxsiz[maxn],totsiz,rt,sta[maxn],tp,ans;
    bool vis[maxn];
    void getroot(rg int now,rg int lat){
    	siz[now]=1;
    	maxsiz[now]=0;
    	for(rg int i=h[now];i!=-1;i=b[i].nxt){
    		rg int u=b[i].to;
    		if(vis[u] || u==lat) continue;
    		getroot(u,now);
    		siz[now]+=siz[u];
    		maxsiz[now]=std::max(maxsiz[now],siz[u]);
    	}
    	maxsiz[now]=std::max(maxsiz[now],totsiz-siz[now]);
    	if(maxsiz[rt]>maxsiz[now]) rt=now;
    }
    void dfs(rg int now,rg int lat,rg int w){
    	sta[++tp]=w;
    	for(rg int i=h[now];i!=-1;i=b[i].nxt){
    		rg int u=b[i].to;
    		if(u!=lat && !vis[u]) dfs(u,now,b[i].val+w);
    	}
    }
    int js(rg int now,rg int w){
    	tp=0;
    	dfs(now,0,w);
    	std::sort(sta+1,sta+tp+1);
    	rg int nans=0;
    	for(rg int i=1,j=tp;;i++){
    		while(j && sta[i]+sta[j]>k) j--;
    		if(i>j) break;
    		nans+=j-i+1;
    	}
    	return nans;
    }
    void dfs2(rg int now){
    	ans+=js(now,0);
    	vis[now]=1;
    	for(rg int i=h[now];i!=-1;i=b[i].nxt){
    		rg int u=b[i].to;
    		if(!vis[u]){
    			ans-=js(u,b[i].val);
    			rt=0;
    			totsiz=siz[u];
    			getroot(u,0);
    			dfs2(rt);
    		}
    	}
    }
    int main(){
    	memset(h,-1,sizeof(h));
    	n=read();
    	rg int aa,bb,cc;
    	for(rg int i=1;i<n;i++){
    		aa=read(),bb=read(),cc=read();
    		ad(aa,bb,cc);
    		ad(bb,aa,cc);
    	}
    	k=read();
    	maxsiz[0]=0x3f3f3f3f,rt=0,totsiz=n;
    	getroot(1,0);
    	dfs2(rt);
    	printf("%d
    ",ans-n);
    	return 0;
    }
    

    但是有些题是取最大值最小值的操作,我们就不能容斥解决,要稍微改变一下写法

    比如 P4149 [IOI2011]Race

    这样写要特判单独一条边的贡献

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    const int maxn=1e6+5;
    int h[maxn],tot=1,n,k;
    struct asd{
    	int to,nxt,val;
    }b[maxn<<1];
    void ad(rg int aa,rg int bb,rg int cc){
    	b[tot].to=bb;
    	b[tot].val=cc;
    	b[tot].nxt=h[aa];
    	h[aa]=tot++;
    }
    int maxsiz[maxn],siz[maxn],totsiz,rt;
    bool vis[maxn];
    void getroot(rg int now,rg int lat){
    	siz[now]=1,maxsiz[now]=0;
    	for(rg int i=h[now];i!=-1;i=b[i].nxt){
    		rg int u=b[i].to;
    		if(u==lat || vis[u]) continue;
    		getroot(u,now);
    		siz[now]+=siz[u];
    		maxsiz[now]=std::max(maxsiz[now],siz[u]);
    	}
    	maxsiz[now]=std::max(maxsiz[now],totsiz-siz[now]);
    	if(maxsiz[rt]>maxsiz[now]) rt=now;
    }
    int sta1[maxn],sta2[maxn],tp,ans=0x3f3f3f3f,mmin[maxn];
    void dfs(rg int now,rg int lat,rg int w1,rg int w2){
    	if(w1>k) return;
    	sta1[++tp]=w1;
    	sta2[tp]=w2;
    	for(rg int i=h[now];i!=-1;i=b[i].nxt){
    		rg int u=b[i].to;
    		if(u!=lat && !vis[u]) dfs(u,now,w1+b[i].val,w2+1);
    	}
    }
    void js(rg int now,rg int w){
    	tp=0,mmin[0]=0;
    	for(rg int i=h[now];i!=-1;i=b[i].nxt){
    		rg int u=b[i].to;
    		if(vis[u]) continue;
    		rg int beg=tp+1;
    		dfs(u,now,b[i].val,1);
    		for(rg int j=beg;j<=tp;j++) ans=std::min(ans,mmin[k-sta1[j]]+sta2[j]);
    		for(rg int j=beg;j<=tp;j++) mmin[sta1[j]]=std::min(mmin[sta1[j]],sta2[j]);
    	}
    	for(rg int i=1;i<=tp;i++) mmin[sta1[i]]=0x3f3f3f3f;
    }
    void dfs2(rg int now){
    	vis[now]=1;
    	js(now,0);
    	for(rg int i=h[now];i!=-1;i=b[i].nxt){
    		rg int u=b[i].to;
    		if(vis[u]) continue;
    		rt=0,totsiz=siz[u];
    		getroot(u,0);
    		dfs2(rt);
    	}
    }
    int main(){
    	memset(mmin,0x3f,sizeof(mmin));
    	memset(h,-1,sizeof(h));
    	n=read(),k=read();
    	rg int aa,bb,cc;
    	for(rg int i=1;i<n;i++){
    		aa=read(),bb=read(),cc=read();
    		aa++,bb++;
    		ad(aa,bb,cc);
    		ad(bb,aa,cc);
    		if(cc==k) ans=1;
    	}
    	totsiz=n,rt=0,maxsiz[0]=0x3f3f3f3f;
    	getroot(1,0);
    	dfs2(rt);
    	if(ans==0x3f3f3f3f) ans=-1;
    	printf("%d
    ",ans);
    	return 0;
    }
    

    整体上点分治的思想还是比较简单的

  • 相关阅读:
    Android学习——SAX解析方式
    Android学习——pull解析方式
    Android学习——使用okhttp
    开课第十五周周总结
    顶会热词3
    顶会热词2
    顶会热词1
    配置JAVA环境
    配置mybaits的一些坑
    Maven配置和idea种tomcat配置的一些坑点
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14180978.html
Copyright © 2020-2023  润新知