2020牛客暑期多校训练营(第八场)A.All-Star Game(线段树+带撤销并查集)

题目链接:https://ac.nowcoder.com/acm/contest/5673/A

 

直接线段树维护每一个关系的生命周期,加一个带撤销并查集,然后dfs更新答案就行了

题目倒是不难写,mark一个带撤销并查集的板子

 

代码:

#include<bits/stdc++.h>
#define xx first
#define yy second
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int MAXN=2e5+5;
map<pii,int> last;
int cnt[MAXN<<1],used[MAXN<<1];
struct UFS
{
	stack<pair<int*, int>> stk;
	int fa[MAXN<<1],rnk[MAXN<<1];
	inline void init(int n)
	{
		for(int i=0;i<=n;i++) fa[i]=i,rnk[i]=0;
	}
	inline int Find(int x)
	{
		while(x^fa[x]) x=fa[x];
		return x;
	}
	inline bool Merge(int x,int y)
	{
		x=Find(x),y=Find(y);
		if(x==y) return false;
		if(rnk[x]<=rnk[y])
		{
			stk.push({fa+x,fa[x]});
			fa[x]=y;
			if(rnk[x]==rnk[y])
			{
				stk.push({rnk+y,rnk[y]});
				rnk[y]++;
			}
		}
		else
		{
			stk.push({fa+y,fa[y]});
			fa[y]=x;
		}
		return true;
	}
	inline void Undo()
	{
		*stk.top().xx=stk.top().yy;
		stk.pop();
	}
}dsu;
struct seg
{
	#define lson l,mid,rt<<1
	#define rson mid+1,r,rt<<1|1
	vector<pii> tr[MAXN<<2];
	int cover,num,player;
	void init(int n,int m)
	{
		cover=m;player=n;num=n+m;
	}
	void update(int L,int R,pii val,int l,int r,int rt)
	{
		if(L>R) return ;
		if(L<=l&&r<=R)
		{
			tr[rt].pb(val);
			return ;
		}
		int mid=(l+r)>>1;
		if(L<=mid)
			update(L,R,val,lson);
		if(mid<R)
			update(L,R,val,rson);
	}
	void doit(int rt)
	{
		for(auto it:tr[rt])
		{
			if(!cnt[it.xx]) cover--;
			if(!used[it.yy]) player--;
			cnt[it.xx]++;
			used[it.yy]++;
			if(dsu.Merge(it.xx,it.yy)) num--;
		}
	}
	void undo(int rt,int sz,int prenum)
	{
		num=prenum;
		for(auto it:tr[rt])
		{
			cnt[it.xx]--;
			used[it.yy]--;
			if(!cnt[it.xx]) cover++;
			if(!used[it.yy]) player++;
		}
		int now=dsu.stk.size();
		while(now>sz)
		{
			dsu.Undo();
			now--;
		}
	}
	void dfs(int l,int r,int rt)
	{
		int sz=dsu.stk.size();
		int prenum=num;
		doit(rt);
		if(l==r)
			printf("%d\n",cover==0?num-player:-1);
		if(l!=r)
		{
			int mid=(l+r)>>1;
			dfs(lson);
			dfs(rson);
		}
		undo(rt,sz,prenum);
	}
}se;
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int n,m,q;
	scanf("%d%d%d",&n,&m,&q);
	dsu.init(n+m);
	se.init(n,m);
	for(int i=1;i<=n;i++)
	{
		int k;
		scanf("%d",&k);
		for(int j=1;j<=k;j++)
		{
			int x;
			scanf("%d",&x);
			last[{x,m+i}]=1;
		}
	}
	for(int i=1;i<=q;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		y+=m;
		if(last[{x,y}])
		{
			int &pos=last[{x,y}];
			se.update(pos,i-1,{x,y},1,q,1);
			pos=0;
		}
		else
		{
			last[{x,y}]=i;
		}
	}
	for(auto it:last)
		if(it.yy)
			se.update(it.yy,q,it.xx,1,q,1);
	se.dfs(1,q,1);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值