A All-Star Game 2020牛客暑期多校训练营(第八场)

https://ac.nowcoder.com/acm/contest/5673/A

队友看错了题,最后半小时跟我们讨论才弄清楚了题意,然后5点10写完1发AC。。。

学习了一下线段树分治,线段树分治有一点像cdq分治,其实就是对于要输出每次操作后的答案的题目,把每次操作后当一个时间点,把这些点当下标,然后把操作进行区间覆盖到这些时间段上面去,最后向下维护线性基或者并查集的同时保存这个操作之前的值丢进栈里面,然后递归完以后把栈里面的值拿出来还原

这题就记录一下每一条边他存在的时间段,然后维护可撤销的并查集,也就是启发式合并的,不能路径压缩的并查集,find一次是logn的,用并查集维护一下当前有n+m个点钟有cnt个连通块,没人喜欢的球员有cnta个,不喜欢任何球员的粉丝有cntb个,那么到最底层的时候,如果cntb>0,那么-1,否则ans[]=cnt-cnta

#include<bits/stdc++.h>
using namespace std;

const int maxl=4e5+10;

map<int,int> mp[maxl];

int n,m,q,k,cnt,cnta,cntb;
struct con
{
	int a,b;
};
struct recv
{
	int a,asz,b,bsz;
	int cnt,cnta,cntb;
};
struct node
{
	int l,r;
	vector<con> e;
	vector<recv> rec;	
}tr[maxl<<2];
int f[maxl],sz[maxl],ans[maxl];

inline void build(int k,int l,int r)
{
	tr[k].l=l;tr[k].r=r;tr[k].e.clear();tr[k].rec.clear();
	if(l==r)
		return;
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}

inline void add(int k,int l,int r,con d)
{
	if(tr[k].l==l && tr[k].r==r)
	{
		tr[k].e.push_back(d);
		return;
	}
	int mid=(tr[k].l+tr[k].r)>>1;
	if(r<=mid)
		add(k<<1,l,r,d);
	else if(l>mid)
		add(k<<1|1,l,r,d);
	else 
		add(k<<1,l,mid,d),add(k<<1|1,mid+1,r,d);
}

inline void prework()
{
	scanf("%d%d%d",&n,&m,&q);
	int a,b;build(1,1,q+1);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&k);
		for(int j=1;j<=k;j++)
		{
			scanf("%d",&b);
			mp[i][b]=1;			
		}
	}
	for(int i=2;i<=q+1;i++)
	{
		scanf("%d%d",&b,&a);
		if(mp[a][b]==0)
			mp[a][b]=i;
		else
		{
			add(1,mp[a][b],i-1,con{a,b+n});
			mp[a][b]=0;
		}
	}
	for(int i=1;i<=n;i++)
		for(auto d:mp[i])
		{
			if(d.second!=0)
				add(1,d.second,q+1,con{i,d.first+n});
		}
}

inline int find(int x)
{
	if(f[x]!=x)
		return find(f[x]);
	return x;
}

inline void solv(int k)
{
	int a,b,aa,bb,len=0;recv tmp;
	for(con d:tr[k].e)
	{
		aa=find(d.a);bb=find(d.b);
		if(aa==bb)
			continue;
		tmp.cnt=cnt;tmp.cnta=cnta;tmp.cntb=cntb;
		--cnt;
		if(sz[aa]==1) --cnta;
		if(sz[bb]==1) --cntb;
		if(sz[aa]<sz[bb]) swap(aa,bb);
		tmp.a=aa;tmp.asz=sz[aa];
		tmp.b=bb;tmp.bsz=sz[bb];
		sz[aa]+=sz[bb];
		f[bb]=aa;
		tr[k].rec.push_back(tmp);++len;
	}
	if(tr[k].l==tr[k].r)
	{
		if(cntb!=0)
			ans[tr[k].l]=-1;
		else
			ans[tr[k].l]=cnt-cnta;	
	}
	else
	{
		solv(k<<1);
		solv(k<<1|1);
	}
	for(int i=len-1;i>=0;i--)
	{
		tmp=tr[k].rec[i];
		cnt=tmp.cnt;cnta=tmp.cnta;cntb=tmp.cntb;
		f[tmp.a]=tmp.a;f[tmp.b]=tmp.b;
		sz[tmp.a]=tmp.asz;sz[tmp.b]=tmp.bsz;
	}
}

inline void mainwork()
{
	for(int i=1;i<=n+m;i++)
	{
		f[i]=i;
		sz[i]=1;
	}
	cnt=n+m;cnta=n;cntb=m;
	solv(1);
}

inline void print()
{
	for(int i=2;i<=q+1;i++)
		printf("%d\n",ans[i]);
}

int main()
{
	prework();
	mainwork();
	print();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/liufengwei1/article/details/107777520