CodeForces - 571D Campus(数据结构综合)

题目链接:点击查看

题目大意:

题目分析:数据结构的题目写起来真好玩~(debug到吐)

考虑离线,题目实质上就是维护两个森林,然后对同一个序列进行的赋值操作,如果是对单一的森林进行加边删边然后连通块内求值修改之类的话,不难想到克鲁斯卡尔重构树,但两个森林的话该怎么办呢

注意到第一个森林中对连通块的操作是加法,第二个森林中对连通块的操作是置零

任取一个点 x 进行讨论,假设现在不考虑第二个森林的贡献,也就是忽略掉第二种操作和第四种操作,并且在每次操作后都记录一下点 x 的值,更具体的,设 val_x[ i ] 是在经过第 i 个操作后,点 x 的值

现在假设第 r 个操作是 “ Q x ”,也就是在第 r 个操作时需要输出 x 的值,但并不是需要输出 val_x[ r ] ,因为 val_x 记录的只是第一个森林的贡献,为了计算总贡献,我们需要知道在第 r 个操作之前,对于点 x 来说距离最近的一次操作 4,也就是第二个森林的置零操作,找到的这个位置记为 l,换句话说,现在在第 l 个操作时将点 x 置零,需要输出第 r 个操作时 x 的值,也就是说在闭区间 [ l + 1 , r ] 内 val_x 的值只受到了第一个森林的影响,到此为止不难想到答案就是 val_x[ r ] - val_x[ l ] 了

综上所述,我们将问题又进行了进一步的转换:

  1. 先通过第二个森林求出每一个操作 Q x 的可行区间 [ l , r ] ,满足:
    1. 当前的操作 Q x 是第 r 次操作
    2. 第 l 次操作是对于点 x 进行的置零操作
    3. 闭区间 [ l + 1 , r ] 内再无对于点 x 进行的置零操作
  2. 再通过第二个森林去维护每个点的 val_x,对于一个操作 Q x 来说,其通过上述操作求出的区间为 [ l , r ] ,那么在其 l 位置减去 val_x[ l ],在其 r 位置减去 val_x[ r ] 就是操作 Q x 的答案

这样就将第一个森林和第二个森林各自的作用区分开来,转换成了两个独立的森林提供的贡献

剩下的用克鲁斯卡尔重构树乱搞就好了

代码:
 

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
     
typedef long long LL;
     
typedef unsigned long long ull;
     
const int inf=0x3f3f3f3f;
   
const int N=1e6+100;

char op[N][2];

int n,m,a[N];

LL ans[N];

vector<int>q[N];

struct Ex_Kruskal
{
	int f[N],L[N],R[N],sz[N],tot,index;
	vector<int>node[N];
	void init(int n)
	{
		tot=0;
		index=n;
		for(int i=0;i<=n<<1;i++)
			f[i]=i;
	}
	int find(int x)
	{
		return f[x]==x?x:f[x]=find(f[x]);
	}
	void addedge(int x,int y)
	{
		int xx=find(x),yy=find(y);
		if(xx!=yy)
		{
			f[xx]=f[yy]=++index;
			node[index].push_back(xx);
			node[index].push_back(yy);
		}
	}
	void dfs(int u)
	{
		L[u]=++tot;
		sz[u]=(u<=n);
		for(auto v:node[u])
		{
			dfs(v);
			sz[u]+=sz[v];
		}
		R[u]=tot;
	}
};

struct Set1
{
	Ex_Kruskal kru;
	int n;
	struct Node
	{
		int l,r;
		LL sum;
	}tree[N<<2];
	void get_dfs()
	{
		n=kru.index;
		for(int i=1;i<=n;i++)
			if(kru.find(i)==i)
				kru.dfs(i);
	}
	void pushdown(int k)
	{
		if(tree[k].sum)
		{
			tree[k<<1].sum+=tree[k].sum;
			tree[k<<1|1].sum+=tree[k].sum;
			tree[k].sum=0;
		}
	}
	void build(int k,int l,int r)
	{
		tree[k].l=l;
		tree[k].r=r;
		tree[k].sum=0;
		if(l==r)
			return;
		int mid=l+r>>1;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
	}
	void update(int k,int l,int r,int val)
	{
		if(tree[k].l>r||tree[k].r<l)
			return;
		if(tree[k].l>=l&&tree[k].r<=r)
		{
			tree[k].sum+=val;
			return;
		}
		pushdown(k);
		update(k<<1,l,r,val);
		update(k<<1|1,l,r,val);
	}
	void change(int x)
	{
		update(1,kru.L[a[x]],kru.R[a[x]],kru.sz[a[x]]);
	}
	LL query(int k,int pos)
	{
		if(tree[k].l==tree[k].r)
			return tree[k].sum;
		pushdown(k);
		int mid=tree[k].l+tree[k].r>>1;
		if(pos<=mid)
			return query(k<<1,pos);
		else
			return query(k<<1|1,pos);
	}
	LL ask(int x)
	{
		return query(1,kru.L[a[x]]);
	}
}t1;

struct Set2
{
	Ex_Kruskal kru;
	int n;
	struct Node
	{
		int l,r;
		int time;
	}tree[N<<2];
	void get_dfs()
	{
		n=kru.index;
		for(int i=1;i<=n;i++)
			if(kru.find(i)==i)
				kru.dfs(i);
	}
	void pushdown(int k)
	{
		if(tree[k].time)
		{
			tree[k<<1].time=tree[k<<1|1].time=tree[k].time;
			tree[k].time=0;
		}
	}
	void build(int k,int l,int r)
	{
		tree[k].l=l;
		tree[k].r=r;
		tree[k].time=0;
		if(l==r)
			return;
		int mid=l+r>>1;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
	}
	void update(int k,int l,int r,int val)
	{
		if(tree[k].l>r||tree[k].r<l)
			return;
		if(tree[k].l>=l&&tree[k].r<=r)
		{
			tree[k].time=val;
			return;
		}
		pushdown(k);
		update(k<<1,l,r,val);
		update(k<<1|1,l,r,val);
	}
	void change(int x)
	{
		update(1,kru.L[a[x]],kru.R[a[x]],x);
	}
	int query(int k,int pos)
	{
		if(tree[k].l==tree[k].r)
			return tree[k].time;
		pushdown(k);
		int mid=tree[k].l+tree[k].r>>1;
		if(pos<=mid)
			return query(k<<1,pos);
		else
			return query(k<<1|1,pos);
	}
	int ask(int x)
	{
		return query(1,kru.L[a[x]]);
	}
}t2;

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	scanf("%d%d",&n,&m);
	t1.kru.init(n),t2.kru.init(n);
	for(int i=1,x,y;i<=m;i++)
	{
		scanf("%s%d",op[i],&x);
		switch(op[i][0])
		{
			case 'U':scanf("%d",&y);t1.kru.addedge(x,y);break; 
			case 'M':scanf("%d",&y);t2.kru.addedge(x,y);break;
			case 'A':a[i]=t1.kru.find(x);break;
			case 'Z':a[i]=t2.kru.find(x);break;
			case 'Q':a[i]=x;break;
		}
	}
	t1.get_dfs(),t2.get_dfs();
	t1.build(1,1,t1.n),t2.build(1,1,t2.n);
	for(int i=1;i<=m;i++)
	{
		switch(op[i][0])
		{
			case 'Z':t2.change(i);break;
			case 'Q':q[t2.ask(i)].push_back(i);break;
		}
	}
	for(int i=1;i<=m;i++)
	{
		switch(op[i][0])
		{
			case 'A':t1.change(i);break;
			case 'Z':for(auto it:q[i])ans[it]-=t1.ask(it);break;
			case 'Q':ans[i]+=t1.ask(i);break;
		}
	}
	for(int i=1;i<=m;i++)
		if(op[i][0]=='Q')
			printf("%lld\n",ans[i]);












    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/109078138
今日推荐