CF571D Campus | 像小小的苍耳从不被人察觉

题目
首先离线,对于操作一、三和操作二、四,分别建出两棵 Kruskal 重构树。
然后问题就转化成在两棵树上分别做子树加一个值和子树赋值的操作。
我们先用一次树上 dfs 处理出每次询问的那个在点在询问时刻之前最后一次被赋值为 0 0 0 的时刻。具体地,dfs 每经过一个点时就在所有以这个点为根的子树的赋值操作的时刻上打 tag,然后对于每一个询问在数据结构上二分即可。这里可以选择树状数组二分或线段树二分。
得到每个询问的点最后一次赋值的时刻后,问题进一步转化成区间求和,再做一遍类似上面的树上 dfs 即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
#define int long long 
#define lc note<<1
#define rc note<<1|1
using namespace std;
const int N=1e6+5;
int n,m,tot1,tot2,x,y,pos;
char opt[5];
int fa1[N],fa2[N],cnt[N],ans[N],las[N];
bool vis[N];
vector<int> c1[N],c2[N],q[N];
inline int read()
{
	int s=0,t=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s*t;
}
struct node
{
	int lson,rson;
}tree1[N],tree2[N];
inline int find1(int s){return fa1[s]==s?s:(fa1[s]=find1(fa1[s]));}
inline int find2(int s){return fa2[s]==s?s:(fa2[s]=find2(fa2[s]));}
struct BIT
{
	int c[N];
	inline void add(int a,int b){for(int i=a+1;i<=m;i+=i&(-i))c[i]+=b;}
	inline int query(int a){int res=0;for(int i=a+1;i;i-=i&(-i))res+=c[i];return res;}
	inline int query(int a,int b){return query(b)-query(a-1);}
}B;
inline void dfs1(int u)
{
	vis[u]=1;
	int sz=c1[u].size();
	for(int i=0;i<sz;++i)
	{
		int tim=c1[u][i];
		B.add(tim,cnt[u]);
	}
	int siz=q[u].size();
	for(int i=0;i<siz;++i)
	{
		int id=q[u][i];
		ans[id]=B.query(las[id]+1,id-1);
	}
	if(tree1[u].lson) dfs1(tree1[u].lson);
	if(tree1[u].rson) dfs1(tree1[u].rson);
	for(int i=0;i<sz;++i)
	{
		int tim=c1[u][i];
		B.add(tim,-cnt[u]);
	}
	return;
}
struct sgt
{
	struct Tree
	{
		int sum;
	}s[N<<2];
	inline void push_up(int note){s[note].sum=s[lc].sum+s[rc].sum;}
	inline void modify(int note,int l,int r,int pos,int val)
	{
		if(l==r){s[note].sum=val;return;}
		int mid=(l+r)>>1;
		if(pos<=mid) modify(lc,l,mid,pos,val);
		else modify(rc,mid+1,r,pos,val);
		push_up(note);
		return;
	}
	inline int calc(int note,int l,int r,int ql,int qr)
	{
		if(ql<=l&&r<=qr) return s[note].sum;
		int mid=(l+r)>>1,res=0;
		if(ql<=mid) res+=calc(lc,l,mid,ql,qr);
		if(qr>mid) res+=calc(rc,mid+1,r,ql,qr);
		return res;
	}
	inline int query(int note,int l,int r,int ql,int qr)
	{
		if(ql<=l&&r<=qr)
		{
			if(l==r)
			{
				if(s[note].sum) return l;
				return 0;
			}
			int mid=(l+r)>>1;
			if(s[rc].sum) return query(rc,mid+1,r,ql,qr);
			else return query(lc,l,mid,ql,qr);
		}
		int mid=(l+r)>>1;
		if(qr>mid&&calc(rc,mid+1,r,ql,qr)) return query(rc,mid+1,r,ql,qr);
		else if(ql<=mid) return query(lc,l,mid,ql,qr);
		return 0; 
	}
}T;
inline void dfs2(int u)
{
	vis[u]=1;
	int sz=c2[u].size();
	for(int i=0;i<sz;++i)
	{
		int tim=c2[u][i];
		T.modify(1,1,m,tim,1);
	}
	if(u<=n)
	{
		int siz=q[u].size();
		for(int i=0;i<siz;++i)
		{
			int id=q[u][i];
			las[id]=T.query(1,1,m,1,id);
		}
	}
	if(tree2[u].lson) dfs2(tree2[u].lson);
	if(tree2[u].rson) dfs2(tree2[u].rson);
	for(int i=0;i<sz;++i)
	{
		int tim=c2[u][i];
		T.modify(1,1,m,tim,0);
	}
}
signed main()
{
	n=read();
	m=read();
	for(int i=1;i<=n;++i) fa1[i]=i,fa2[i]=i,cnt[i]=1;
	tot1=tot2=n;
	for(int i=1;i<=m;++i)
	{
		scanf("%s",opt+1);
		if(opt[1]=='U')
		{
			x=read();
			y=read();
			++tot1;
			fa1[tot1]=tot1;
			tree1[tot1].lson=find1(x);
			tree1[tot1].rson=find1(y);
			cnt[tot1]=cnt[find1(x)]+cnt[find1(y)];
			fa1[find1(x)]=fa1[find1(y)]=tot1;
		}
		if(opt[1]=='M')
		{
			x=read();
			y=read();
			++tot2;
			fa2[tot2]=tot2;
			tree2[tot2].lson=find2(x);
			tree2[tot2].rson=find2(y);
			fa2[find2(x)]=fa2[find2(y)]=tot2;
		}
		if(opt[1]=='A')
		{
			pos=read();
			c1[find1(pos)].push_back(i);
		}
		if(opt[1]=='Z')
		{
			pos=read();
			c2[find2(pos)].push_back(i);
		}
		if(opt[1]=='Q')
		{
			pos=read();
			q[pos].push_back(i);
		}
	}
	for(int i=tot2;i;--i)
		if(!vis[i]) dfs2(i);
	for(int i=1;i<=m;++i) ans[i]=-1;
	for(int i=1;i<=tot1;++i) vis[i]=0;
	for(int i=tot1;i;--i)
		if(!vis[i]) dfs1(i);
	for(int i=1;i<=m;++i)
		if(ans[i]!=-1) printf("%lld\n",ans[i]);
	return 0;
}
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值