……

算法学习:树上差分

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

\(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\n",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("\n");
	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<<"\n";
	for(int i=1;i<=q;i++)
	{
		int a=rand()%n+1,b=rand()%n+1;
		cout<<a<<" "<<b<<"\n";
	}
	return 0;
}

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

posted @ 2020-02-18 12:41  童话镇里的星河  阅读(178)  评论(0编辑  收藏  举报