CF1556G Gates to Another World

一、题目

点此看题

二、解法

考虑本题数据范围极大并且删除操作是以区间的形式给出的,那么可以考虑动态开点线段树

继续考虑如果我们用线段树做有什么性质,其实就是把左儿子和右儿子的叶子节点对应连边,简而言之,我们把它转化成了一个对应点优化连边问题

你可能会联想到萌萌哒那道题,所以我们不在线维护连通块,而在读入所有询问修改之后做优化连边。具体来说我们把每个点打上一个删除标记 \(t_i\),表示删除这个点的时间,如果未被删除那么 \(t_i=m+1\)

那么最后应该如何连边呢?考虑一个"叶节点"(并不是真正的叶子,而是动态开点之后没有儿子的结点)中所有叶节点的行为平行,因为它们删除的时间相同,并且一定联通,所有可以把他们当成一个点来考虑。

加入上述优化之后复杂度就正确了,也就是对于每个非叶节点一直递归下去,直到找到两个叶子连边,这条边被删除的时间是 \(\min(t_x,t_y)\),求出所有边之后我们离线逆序加边,用并查集判断连通性即可。

因为每个叶子只会在 \(n\) 个祖先处有 \(1\) 的贡献,所以总时间复杂度 \(O(n^2m)\),空间复杂度也是 \(O(n^2m)\),要注意卡空间。

三、总结

优化对应点连边的方法是用数据结构维护某个量,读入所有修改后再优化建图。

较大数据范围的解决方案:寻找等价类(本题的等价类就是"叶节点")

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 50005;
const int N = 100*M;
#define Int long long
#define pii pair<int,int>
#define mp make_pair
Int read()
{
	Int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
Int n,m,k,rt,cnt,ls[N],rs[N],rm[N],a[M],b[M],fa[N];
vector<pii> e[M];Int ans[M],pd[M];
void down(Int x)
{
	if(!rm[x]) return ;
	if(!ls[x]) ls[x]=++cnt;
	if(!rs[x]) rs[x]=++cnt;
	rm[ls[x]]=rm[rs[x]]=rm[x];rm[x]=0;
}
void ins(Int &x,Int l,Int r,Int L,Int R,Int c)
{
	if(!x) x=++cnt;
	if(L>r || l>R) return ;
	if(L<=l && r<=R)
	{
		rm[x]=c;
		return ;
	}
	Int mid=(l+r)>>1;down(x);
	ins(ls[x],l,mid,L,R,c);
	ins(rs[x],mid+1,r,L,R,c);
}
Int leaf(Int x) {return !ls[x] && !rs[x];}
Int loc(Int x,Int l,Int r,Int id)
{
	if(leaf(x)) return x;
	Int mid=(l+r)>>1;
	if(mid>=id) return loc(ls[x],l,mid,id);
	return loc(rs[x],mid+1,r,id);
}
Int find(Int x)
{
	if(fa[x]!=x) fa[x]=find(fa[x]);
	return fa[x];
}
void fuck(Int x,Int y)
{
	if(leaf(x) && leaf(y))
	{
		e[min(rm[x],rm[y])].push_back(mp(x,y));
		return ;
	}
	if(leaf(x))
	{
		fuck(x,ls[y]);fuck(x,rs[y]);
		return ;
	}
	if(leaf(y))
	{
		fuck(ls[x],y);fuck(rs[x],y);
		return ;
	}
	fuck(ls[x],ls[y]);
	fuck(rs[x],rs[y]);
}
signed main()
{
	n=read();m=read();k=(1ll<<n)-1;
	rm[rt=cnt=1]=m+1;
	for(Int i=1;i<=m;i++)
	{
		string s;cin>>s;
		Int l=read(),r=read();
		if(s[0]=='a') a[i]=l,b[i]=r,pd[i]=1;
		else ins(rt,0,k,l,r,i);
	}
	for(Int i=1;i<=cnt;i++)
		fa[i]=i;
	for(Int i=1;i<=cnt;i++)
		if(!leaf(i)) fuck(ls[i],rs[i]);
	for(auto x:e[m+1])
		fa[find(x.first)]=find(x.second);
	for(Int i=m;i>=1;i--)
	{
		for(auto x:e[i])
			fa[find(x.first)]=find(x.second);
		ans[i]=find(loc(rt,0,k,a[i]))
		==find(loc(rt,0,k,b[i]));
	}
	for(Int i=1;i<=m;i++)
		if(pd[i]) printf("%lld\n",ans[i]);
}
posted @ 2021-09-30 15:32  C202044zxy  阅读(152)  评论(0编辑  收藏  举报