• 算法学习:树上差分


    初步学习了树上差分,这里主要是些简单的例题。

    (Part1). 树上点的差分:

    题目链接:P3128 [USACO15DEC]最大流Max Flow
    还以为是网络流
    点的差分很简单,就是树剖不配线段树了,一个差分数组就够了:
    复杂度(O(nlogn+k+n))(大概是),可以通过本题。

    (Code):

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int MAXN=500005;
    int f[MAXN],top[MAXN],tot[MAXN],son[MAXN],deep[MAXN];
    int rt[MAXN];
    int id[MAXN],c=0;
    int n,m,l,r;
    struct node
    {
    	int to,nxt;
    }e[MAXN<<1];
    int head[MAXN],cnt=0;
    void add(int u,int v)
    {
    	e[++cnt].to=v;
    	e[cnt].nxt=head[u];
    	head[u]=cnt;
    }
    int dfs1(int cur,int fa,int step)
    {
    	deep[cur]=step;
    	f[cur]=fa;
    	tot[cur]=1;
    	int maxn=-1;
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(j!=fa)
    		{
    			tot[cur]+=dfs1(j,cur,step+1);
    			if(tot[j]>maxn) maxn=tot[j],son[cur]=j;
    		}
    	}
    	return tot[cur];
    }
    void dfs2(int cur,int topf)
    {
    	top[cur]=topf;
    	id[cur]=++c;
    	if(!son[cur]) return;
    	dfs2(son[cur],topf);
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(!id[j]) dfs2(j,j);
    	}
    }
    void D_in_tree(int l,int r)
    {
    	while(top[l]!=top[r])
    	{
    		if(deep[top[l]]<deep[top[r]]) swap(l,r);
    		rt[id[l]+1]--;
    		rt[id[top[l]]]++;
    		l=f[top[l]];
    	}
    	if(deep[l]<deep[r]) swap(l,r);
    	rt[id[r]]++;
    	rt[id[l]+1]--;
    	return;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d",&l,&r);
    		add(l,r);
    		add(r,l);
    	}
    	dfs1(1,1,1);
    	dfs2(1,1);
    	for(int i=1;i<=n;i++) rt[i]=0;
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d",&l,&r);
    		D_in_tree(l,r);
    		//for(int i=1;i<=n;i++) cout<<rt[i]<<" ";
    		//cout<<endl;
    	}
    	int last=0,ans=-1;
    	/*for(int i=1;i<=n;i++) cout<<id[i]<<" ";
    	cout<<endl;
    	for(int i=1;i<=n;i++) cout<<top[i]<<" ";
    	cout<<endl;
    	for(int i=1;i<=n;i++) cout<<deep[i]<<" ";
    	cout<<endl;
    	for(int i=1;i<=n;i++) cout<<rt[i]<<" ";
    	cout<<endl;*/
    	for(int i=1;i<=n;i++)
    	{
    		rt[i]=last+rt[i];
    		last=rt[i];
    		ans=max(ans,last);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    /*
    6 6
    1 2
    3 1
    1 4
    3 5
    6 3
    2 4
    2 5
    5 6
    1 3
    5 4
    6 3
    */
    

    当然有很多的调试语句,突出惨烈性,又附了组数据。

    (Part2).树上边的差分

    题目链接:CF191C Fools and Roads
    大家都用的(lca),而我有一个好法子
    这棵树是无根树,但并不影响答案,所以我们认为她是有根树(以1为根),这样我们发现可对边重新编号:
    即边的序号为连接深度较大的节点编号,显然为(2,3......n).
    于是差分即可:

    (Code):

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int MAXN=1000005;
    int f[MAXN],top[MAXN],tot[MAXN],son[MAXN],deep[MAXN];
    int rt[MAXN];
    int id[MAXN],c=0;
    int n,m,l,r;
    struct node
    {
    	int to,nxt,ans;
    	node()
    	{
    		ans=-1;
    	}
    }e[MAXN<<1];
    int head[MAXN],cnt=0;
    void add(int u,int v)
    {
    	e[++cnt].to=v;
    	e[cnt].nxt=head[u];
    	head[u]=cnt;
    }
    int dfs1(int cur,int fa,int step)
    {
    	deep[cur]=step;
    	f[cur]=fa;
    	tot[cur]=1;
    	int maxn=-1;
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(j!=fa)
    		{
    			tot[cur]+=dfs1(j,cur,step+1);
    			if(tot[j]>maxn) maxn=tot[j],son[cur]=j;
    		}
    	}
    	return tot[cur];
    }
    void dfs2(int cur,int topf)
    {
    	top[cur]=topf;
    	id[cur]=++c;
    	if(!son[cur]) return;
    	dfs2(son[cur],topf);
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(!id[j]) dfs2(j,j);
    	}
    }
    void D_in_tree(int l,int r)
    {
    	while(top[l]!=top[r])
    	{
    		if(deep[top[l]]<deep[top[r]]) swap(l,r);
    		rt[id[l]+1]--;
    		rt[id[top[l]]]++;
    		l=f[top[l]];
    	}
    	if(deep[l]<deep[r]) swap(l,r);
    	rt[id[son[r]]]++;
    	rt[id[l]+1]--;
    	return;
    }
    void work(int cur,int fa)
    {
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(j==fa) continue;
    		e[i].ans=rt[id[j]];
    		work(j,cur);
    	}
    }
    int main()
    {
    	//freopen("data.in","r",stdin);
    	//freopen("baoli.out","w",stdout);
    	scanf("%d",&n);
    	if(n==0)
    	{
    		cout<<0;
    		return 0;
    	}
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d",&l,&r);
    		add(l,r);
    		add(r,l);
    	}
    	dfs1(1,1,1);
    	dfs2(1,1);
    	for(int i=1;i<=n;i++) if(!son[i]) son[i]=i;
    	scanf("%d",&m);
    	for(int i=1;i<=n;i++) rt[i]=0;
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d",&l,&r);
    		if(l!=r) D_in_tree(l,r);
    	}
    	for(int i=1;i<=cnt;i++) e[i].ans=-1;
    	work(1,1);
    	for(int i=1;i<=cnt;i++) if(e[i].ans>=0) printf("%d ",e[i].ans);
    	printf("
    ");
    	return 0;
    }
    

    似乎和(lys)大佬数据对拍有锅,但我也不太确定是不是对拍器炸了(但愿是)。
    事实证明,空间与时间都比(lys)大佬的差三倍(qwq)
    反正难写就是了,
    再不会(Crayon)的前提下,(K...)算法随机生成出了锅,鸣谢一位大佬的程序,附上:

    (Code):

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,cnt,fa[100015];
    int find(int x){
    	return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    int main()
    {
    	freopen("data.in","w",stdout);
    	srand((unsigned)time(NULL));
    	//srand(time(0));
    	//int n=rand()%6+1;
    	int n=rand()%30+2;
    	cout<<n<<endl;
    	for(int i=1;i<=n;i++)fa[i]=i;
    	while(cnt<n-1){
    		int x=rand()*rand(),y=rand()*rand();
    		x=x%n+1;
    		y=y%n+1;
    		int x1=find(x),y1=find(y);
    		if(x1!=y1) fa[x1]=y1,cnt++,cout<<x<<" "<<y<<endl;
    	}
    	int q=rand()%75+1;
    	cout<<q<<"
    ";
    	for(int i=1;i<=q;i++)
    	{
    		int a=rand()%n+1,b=rand()%n+1;
    		cout<<a<<" "<<b<<"
    ";
    	}
    	return 0;
    }
    
    

    这样并查集就实现了随机生成树

  • 相关阅读:
    Dll 入口函数
    .net中Global.asax
    jquery图片翻转
    c#发送邮件
    jquery ajax里面的datetype设成json时,提交不了数据的问题
    今儿写前台tab效果时用的,(jquery)
    C# 的一些常用日期时间函数(老用不熟)
    下午的表单注册~~~
    CSS图片、文字垂直居中对齐
    asp.net 用继承方法实现页面判断session
  • 原文地址:https://www.cnblogs.com/tlx-blog/p/12325721.html
Copyright © 2020-2023  润新知