【BZOJ3514】Codechef MARCH14 GERALD07加强版(LCT)(主席树)

34 篇文章 0 订阅

传送门


题解:

对于一堆不成环的边,显然连通块数量就是 n − n- n边数。

LCT维护边标号的最大生成树,主席树维护一下有哪些边存在就行了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int N=2e5+7;

int n,m,q,op;
struct edge{int u,v;}e[N];
namespace PST{
	cs int N=::N*80;
	int lc[N],rc[N],sum[N],now;
	inline void ins(int &rt,int l,int r,int p,int v){
		++now;
		lc[now]=lc[rt];
		rc[now]=rc[rt];
		sum[now]=sum[rt]+v;rt=now;
		if(l==r)return ;
		int mid=l+r>>1;
		(p<=mid)?ins(lc[rt],l,mid,p,v):ins(rc[rt],mid+1,r,p,v);
	}
	inline int query(int rt,int l,int r,int pre){
		if(pre<=l||!rt)return sum[rt];
		int mid=l+r>>1;
		if(mid<pre)return query(rc[rt],mid+1,r,pre);
		return query(lc[rt],l,mid,pre)+sum[rc[rt]];
	}
}

namespace LCT{
	cs int N=::N<<1;
	int fa[N],son[N][2],mn[N],rev[N];
	inline void init(int n){for(int re i=1;i<=n;++i)mn[i]=i;mn[0]=0x3f3f3f3f;}
	inline void pushup(int u){mn[u]=std::min(u,std::min(mn[son[u][0]],mn[son[u][1]]));}
	inline void pushdown(int u){
		if(rev[u]){
			std::swap(son[u][0],son[u][1]);rev[u]=0;
			rev[son[u][0]]^=1;rev[son[u][1]]^=1;
		}
	}
	inline bool isrt(int u){return son[fa[u]][0]!=u&&son[fa[u]][1]!=u;}
	inline bool which(int u){return son[fa[u]][1]==u;}
	inline void Rotate(int u){
		int p=fa[u],pp=fa[p],d=which(u);
		if(!isrt(p))son[pp][which(p)]=u;
		fa[u]=pp,fa[p]=u,fa[son[u][!d]]=p;
		son[p][d]=son[u][!d],son[u][!d]=p;
		pushup(p);pushup(u);
	}
	inline void Splay(int u){
		static int q[N],qn;
		for(int re p=q[qn=1]=u;!isrt(p);p=fa[p])q[++qn]=fa[p];
		while(qn)pushdown(q[qn--]);
		for(int re p=fa[u];!isrt(u);Rotate(u),p=fa[u])
		if(!isrt(p))Rotate(which(u)==which(p)?p:u);
	}
	inline int access(int u){
		int ch;
		for(ch=0;u;u=fa[ch=u])
		Splay(u),son[u][1]=ch,pushup(u);
		return ch;
	}
	inline void makert(int u){
		rev[access(u)]^=1;Splay(u);
	}
	inline int find_rt(int u){
		for(u=access(u);pushdown(u),son[u][0];u=son[u][0]);
		Splay(u);return u;
	}
	inline void cut(int u,int v){
		makert(u);access(v);Splay(v);
		fa[son[v][0]]=0;son[v][0]=0;pushup(v);
	}
	inline void link(int u,int v){
		makert(u);fa[u]=v;
	}
	inline int calc(int u,int v){
		makert(u);return (find_rt(v)!=u)?0:mn[u];
	}
}

int rt[N];
signed main(){
#ifdef zxyoi
	freopen("gearl.in","r",stdin);
#endif
	n=gi(),m=gi(),q=gi(),op=gi();LCT::init(n+m);
	for(int re i=1;i<=m;++i){
		e[i].u=gi()+m,e[i].v=gi()+m;
		rt[i]=rt[i-1];if(e[i].u==e[i].v)continue;
		PST::ins(rt[i],1,m,i,1);
		int p=LCT::calc(e[i].u,e[i].v);
		if(p)PST::ins(rt[i],1,m,p,-1),LCT::cut(p,e[p].u),LCT::cut(p,e[p].v);
		LCT::link(i,e[i].u);
		LCT::link(i,e[i].v);
	}
	int ans=0;
	while(q--){
		int l=gi()^ans,r=gi()^ans;
		cout<<(ans=n-PST::query(rt[r],1,m,l))<<"\n";
		ans*=op;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值