zoukankan      html  css  js  c++  java
  • 点分治总结

    为了避免变量名指代不清的问题,我们先规定一下各变量的含义。

    const int N = 10005;
    struct edge{int to,next,w;}a[N<<1];//边集数组
    int n,k,head[N],cnt;//n,k不解释,head[]和cnt是边集数组的辅助变量 
    int root,sum;//当前找到的根,当前递归这棵树的大小 
    int vis[N];//某一个点是否被当做根过 
    int sz[N];//每个点下面的大小 
    int f[N];//每个点为根时的最大子树大小 
    int dep[N];//每个点的深度 
    int o[N];//每个点的深度(用于排序) (这个是以poj1741为例,其他题目不一定要用到这个)
    int ans;//最终统计的答案 
    

    点分治的核心是找一个点作为根,而找出来的这个点就是我们所说的“重心”。

    void getroot(int u,int fa)
    {
    	sz[u]=1;f[u]=0;
    	for (int e=head[u];e;e=a[e].next)
    	{
    		int v=a[e].to;if (v==fa||vis[v]) continue;
    		getroot(v,u);
    		sz[u]+=sz[v];f[u]=max(f[u],sz[v]); 
    	}
    	f[u]=max(f[u],sum-sz[u]);
    	if (f[u]<f[root]) root=u;
    }
    

    每次找出一个根以后,所有点对就只有两种可能了:
    1、两个点都在根的某一棵子树中,即路径不过根;
    2、两个点在根的不同子树中,或其中一个点就是根,此时路径必过根。
    对于case1显然递归处理,对于case2在此处计算。
    没什么好说的看代码吧。

    POJ1741 Tree

    http://poj.org/problem?id=1741
    给出一棵树,给一个K值,求一共有多少点对满足(dis(u,v)<=K)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 10005;
    struct edge{int to,next,w;}a[N<<1];
    int n,m,k,head[N],cnt,root,sum,vis[N],sz[N],f[N],dep[N],o[N],ans;
    int gi()
    {
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    void getroot(int u,int fa)
    {
    	sz[u]=1;f[u]=0;
    	for (int e=head[u];e;e=a[e].next)
    	{
    		int v=a[e].to;if (v==fa||vis[v]) continue;
    		getroot(v,u);
    		sz[u]+=sz[v];
    		f[u]=max(f[u],sz[v]);
    	}
    	f[u]=max(f[u],sum-sz[u]);
    	if (f[u]<f[root]) root=u;
    }
    void getdeep(int u,int fa)
    {
    	o[++cnt]=dep[u];
    	for (int e=head[u];e;e=a[e].next)
    	{
    		int v=a[e].to;if (v==fa||vis[v]) continue;
    		dep[v]=dep[u]+a[e].w;getdeep(v,u);
    	}
    }
    int calc(int u,int d0)
    {
    	cnt=0;dep[u]=d0;
    	getdeep(u,0);
    	sort(o+1,o+cnt+1);
    	int l=1,r=cnt,res=0;
    	while (l<r)
    		if (o[l]+o[r]<=k) res+=r-l,l++;
    		else r--;
    	return res;
    }
    void solve(int u)
    {
    	ans+=calc(u,0);
    	vis[u]=1;
    	for (int e=head[u];e;e=a[e].next)
    	{
    		int v=a[e].to;if (vis[v]) continue;
    		ans-=calc(v,a[e].w);
    		sum=sz[v];root=0;
    		getroot(v,0);
    		solve(root);
    	}
    }
    int main()
    {
    	while (1)
    	{
    		n=gi();k=gi();
    		if (n==0&&k==0) return 0;
    		memset(head,0,sizeof(head));
    		memset(vis,0,sizeof(vis));
    		cnt=0;ans=0;
    		for (int i=1,u,v,w;i<n;i++)
    		{
    			u=gi();v=gi();w=gi();
    			a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
    			a[++cnt]=(edge){u,head[v],w};head[v]=cnt;
    		}
    		root=0;sum=f[0]=n;
    		getroot(1,0);
    		solve(root);
    		printf("%d
    ",ans);
    	}
    }
    

    Luogu3806 【模板】点分治1

    https://www.luogu.org/problemnew/show/P3806

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N = 10005;
    struct edge{int to,next,w;}a[N<<1];
    int n,m,k,head[N],cnt,root,sum,vis[N],sz[N],f[N],dep[N],o[N],ans[10000005];
    int gi()
    {
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    void getroot(int u,int fa)
    {
    	sz[u]=1;f[u]=0;
    	for (int e=head[u];e;e=a[e].next)
    	{
    		int v=a[e].to;if (v==fa||vis[v]) continue;
    		getroot(v,u);
    		sz[u]+=sz[v];f[u]=max(f[u],sz[v]); 
    	}
    	f[u]=max(f[u],sum-sz[u]);
    	if (f[u]<f[root]) root=u;
    }
    void getdeep(int u,int fa)
    {
    	o[++cnt]=dep[u];
    	for (int e=head[u];e;e=a[e].next)
    	{
    		int v=a[e].to;if (v==fa||vis[v]) continue;
    		dep[v]=dep[u]+a[e].w;getdeep(v,u);
    	}
    }
    void calc(int u,int d0,int add)
    {
    	cnt=0;dep[u]=d0;
    	getdeep(u,0);
    	for (int i=1;i<=cnt;i++)
    		for (int j=1;j<=cnt;j++)
    			ans[o[i]+o[j]]+=add;
    }
    void solve(int u)
    {
    	calc(u,0,1);vis[u]=1;
    	for (int e=head[u];e;e=a[e].next)
    	{
    		int v=a[e].to;if (vis[v]) continue;
    		calc(v,a[e].w,-1);
    		sum=sz[v];root=0;
    		getroot(v,0);
    		solve(root);
    	}
    }
    int main()
    {
    	n=gi();m=gi();
    	for (int i=1,u,v,w;i<n;i++)
    	{
    		u=gi();v=gi();w=gi();
    		a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
    		a[++cnt]=(edge){u,head[v],w};head[v]=cnt;
    	}
    	sum=f[0]=n;
    	getroot(1,0);
    	solve(root);
    	for (int i=1;i<=m;i++)
    		k=gi(),puts(ans[k]?"AYE":"NAY");
    	return 0;
    }
    

    聪聪可可

    https://www.luogu.org/problemnew/show/2634
    给一棵树,求这棵树上任选两个点(注意可以相同)使得它们距离为3的倍数的概率。
    点分治每次求出到当前根距离除以3余0,1,2的点的数量(t_0t_1t_2)然后答案就是(t_0*t_0+2*t_1*t_2)
    概率就是总方案数除以(n^2)再约个分。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 20005;
    struct edge{int to,next,w;}a[N<<1];
    int n,head[N],cnt,root,sum,vis[N],sz[N],f[N],dep[N],t[3],ans;
    int gcd(int a,int b){return b?gcd(b,a%b):a;}
    int gi()
    {
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    void getroot(int u,int fa)
    {
    	sz[u]=1;f[u]=0;
    	for (int e=head[u];e;e=a[e].next)
    	{
    		int v=a[e].to;if (v==fa||vis[v]) continue;
    		getroot(v,u);
    		sz[u]+=sz[v];f[u]=max(f[u],sz[v]);
    	}
    	f[u]=max(f[u],sum-sz[u]);
    	if (f[u]<f[root]) root=u;
    }
    void getdeep(int u,int fa)
    {
    	t[dep[u]%3]++;
    	for (int e=head[u];e;e=a[e].next)
    	{
    		int v=a[e].to;if (v==fa||vis[v]) continue;
    		dep[v]=dep[u]+a[e].w;getdeep(v,u);
    	}
    }
    int calc(int u,int d0)
    {
    	dep[u]=d0;t[0]=t[1]=t[2]=0;
    	getdeep(u,0);
    	return t[0]*t[0]+2*t[1]*t[2];
    }
    void solve(int u)
    {
    	ans+=calc(u,0);vis[u]=1;
    	for (int e=head[u];e;e=a[e].next)
    	{
    		int v=a[e].to;if (vis[v]) continue;
    		ans-=calc(v,a[e].w);
    		sum=sz[v];root=0;
    		getroot(v,0);
    		solve(root);
    	}
    }
    int main()
    {
    	n=gi();
    	for (int i=1;i<n;i++)
    	{
    		int u=gi(),v=gi(),w=gi();
    		a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
    		a[++cnt]=(edge){u,head[v],w};head[v]=cnt;
    	}
    	f[0]=sum=n;
    	getroot(1,0);
    	solve(root);
    	int zsy=gcd(ans,n*n);
    	printf("%d/%d
    ",ans/zsy,n*n/zsy);
    	return 0;
    }
    
    
  • 相关阅读:
    Note:《Microsoft Windows Workflow Foundation 入门:开发人员演练》
    泛型集合类型,赋予集合业务意义,增强集合的抽象使用
    IIS7.0 for developer
    【代码保留】成对值类(PairCollection和Pair
    《SOA中国路线图》下载
    【代码保留】Quarter类
    复合控件和事件(6)——一点优化
    全方位掌握 NSIS 的使用[转]
    HTML Entities Examples
    如何对Outlook添加右键菜单
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/8137645.html
Copyright © 2011-2022 走看看