CF571D

离线并查集O(nlogn)做法

首先,因为题目中有A,B两个并查集,显然不能同时维护权值。所以我们考虑离线处理。

对于一个查询Query x来说,设当前时间戳为t1,我们只用找到关于x时间戳距离当前最近的清零操作,设它的时间戳是t2,
则一个Add操作(设时间戳为t3)跟当前查询有贡献当且仅当:

t2<t3<t1

然后我们发现答案具有可减性,即我们可以算出t2时刻C[x]的值的值再算出t1时刻C[x]的值,相减即可。

问题就是怎么算t2,我们考虑并查集。
我们对于每个清零操作,显然不可能暴力清零 (废话)

考虑在并查集的根节点打标记,打上当前的时间戳t。每次查询时,从x暴力向上跳(因为并查集按秩合并深度为log级别,所以一次操作复杂度log),取所有的时间戳的max。

对于合并操作,直接按秩合并即可。即深度小的接到深度大的上。

但是也会有问题
比如下面的数据:

Z 1

M 1 2

Q 2

我们打标记的时候由于可能本来的深度大的并查集的根就打了标记,但对新连的没有贡献。而且这个操作不具有可减性。

所以我们对每个点记录一个自己连到父亲的时候的时间戳t2.

然后暴力往上跳的时候顺便维护t2的min,然后当前的t小于t2就没贡献。复杂度(O(nlogn))


现在我们只剩1,3,4操作了

我们用类似的思路,再次用并查集按秩合并。

我们仍然在根节点打上加法懒惰标记。同样会遇到相同的问题,但这次会好做,因为加法可以差分。我们每次连的时候直接连,设v连向u,则lazy[v]-=lazy[u],就可以直接维护。

查询依然是暴力向上跳。

复杂度O(nlogn)


最慢的点只跑了150ms,这就是小常数吗

代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int f[500005];
int siz[500005];
int dep[500005];
long long val[500005];
long long val2[500005]; 
long long last[500005];
int Find(int F){
	if(f[F]==F)return F;
	return Find(f[F]); 
} 
void _union2(int u,int v){
	int f1=Find(u),f2=Find(v);
	if(f1==f2)return;
	if(siz[f1]<siz[f2])swap(f1,f2);
	f[f2]=f1;siz[f1]+=siz[f2];val[f2]-=val[f1];
	return;
}
void _union1(int u,int v,int t){
	int f1=Find(u),f2=Find(v);
	if(f1==f2)return;
	if(dep[f1]<dep[f2])swap(f1,f2);
	f[f2]=f1;siz[f1]+=siz[f2];dep[f1]=max(dep[f1],dep[f2]+1);val2[f2]=t;
	return;
}
int x[500005],y[500005];
long long ans[500005];
char c[500005];
priority_queue<pair<int,int> >Q;
inline int read()
{
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
signed main(){
	n=read();
	m=read();
	for(int i=1;i<=n;i++)f[i]=i,siz[i]=1,val[i]=0,dep[i]=1; 
	for(int i=1;i<=m;i++){
		c[i]=getchar();
		while(c[i]!='U'&&c[i]!='M'&&c[i]!='A'&&c[i]!='Z'&&c[i]!='Q')c[i]=getchar();
		if(c[i]=='U'||c[i]=='M')
			x[i]=read(),y[i]=read();
		else x[i]=read();
	}
	int G=0;
	for(int i=1;i<=m;i++){
		if(c[i]=='M'){
			_union1(x[i],y[i],i);
		}
		if(c[i]=='Z'){
			val[Find(x[i])]=i;
		}
		if(c[i]=='Q'){
			last[i]=val[x[i]];
			int F=x[i];
			long long atleast=val2[x[i]];
			while(f[F]!=F){
				F=f[F]; 
				if(val[F]<atleast){
					atleast=max(val2[F],atleast);
					continue;
				}
				atleast=max(val2[F],atleast);
				last[i]=max(last[i],val[F]);
			}
			G++;
			Q.push(make_pair(-i,i));
			if(last[i]!=0)Q.push(make_pair(-last[i],-i));
		}
	}
	for(int i=1;i<=n;i++)f[i]=i,siz[i]=1,val[i]=0,dep[i]=1;
	for(int i=1;i<=m;i++){
		if(c[i]=='U')
			_union2(x[i],y[i]);
		if(c[i]=='A')
			val[Find(x[i])]+=siz[Find(x[i])];
		while(!Q.empty()&&Q.top().first==-i){
			int u=Q.top().second;
			Q.pop();
			long long xs=1;
			if(u<0)u=-u,xs=-1;
			int F=x[u];
			ans[u]+=xs*val[F];
			while(f[F]!=F){
				F=f[F];
				ans[u]=ans[u]+xs*val[F];
			}
		}
	}
	for(int i=1;i<=m;i++)
		if(c[i]=='Q')printf("%lld
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/Kiana-Kaslana/p/15541069.html