bzoj3514: Codechef MARCH14 GERALD07加强版

传送门
容易想到莫对+并查集乱搞,强制在线就分块+并查集乱搞,时空复杂度好像都不太行的样子。
我们维护一个类似并查集的树形结构,加入一条边i,若这条边的两端点已经联通,找到这个环上最早的一条边j,那么当询问区间左端点在i之前时,当且仅当右端点延伸到i之后,删掉j这条边并不改变区间的连通性(j~i不存在边可以替换j维持原有的连通性),直接在树上用当前边i替换边j,且记录下pr[i]=j。
查询时找到区间[l,r]内pr<l的边的数目,就是区间并查集中的边数,联通块数即n-边数。

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#define For(i,a,b) for(register int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(register int i=(a);i>=(b);i--)
#define Formylove return 0
#define pi acos(-1.0)
const int N=200007*2;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,q,type,pr[N],ex[N],ey[N];

template<typename T> void read(T &x) {
	char ch=getchar(); T f=1; x=0;
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') f=-1,ch=getchar();
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

#define lc ch[x][0]
#define rc ch[x][1]
#define mid ((l+r)>>1)

struct lct{
	int p[N],ch[N][2],mi[N],tot,flip[N];
	int isroot(int x) { return (x!=ch[p[x]][0])&&(x!=ch[p[x]][1]); }
	
	void upd(int x) {
		mi[x]=x; 
		if(lc) mi[x]=min(mi[x],mi[lc]);
		if(rc) mi[x]=min(mi[x],mi[rc]);
	}
	
	void down(int x) {
		if(!flip[x]) return;
		swap(lc,rc);
		flip[lc]^=1;
		flip[rc]^=1;
		flip[x]^=1;
	}
	
	void rotate(int x) {
		int y=p[x],z=p[y],l=(x==ch[y][1]),r=(l^1);
		if(!isroot(y)) ch[z][y==ch[z][1]]=x; p[x]=z;
		ch[y][l]=ch[x][r]; p[ch[x][r]]=y;
		ch[x][r]=y; p[y]=x;
		upd(y); upd(x);
	}
	
	void splay(int x) {
		static int sta[N],top,tp; 
		tp=x; sta[top=1]=tp;
		for(;!isroot(tp);tp=p[tp]) sta[++top]=p[tp]; 
		while(top) down(sta[top--]);
		for(;!isroot(x);rotate(x)) {
			int y=p[x],z=p[y];
			if(!isroot(y)) ((x==ch[y][1])^(y==ch[z][1]))?rotate(x):rotate(y);
		}
	}
	
	void access(int x) {
		for(int t=0;x;x=p[t=x]) {
			splay(x);
			rc=t;
			upd(x);
		}
	}
	
	void newroot(int x) {
		access(x);
		splay(x);
		flip[x]^=1;
	}
	
	int find_root(int x) {
		access(x);
		splay(x);
		while(lc) x=lc;
		return x;
	}
	
	void lik(int x,int y) {
		newroot(x);
		splay(x); splay(y);
		p[x]=y;
	}
	
	void cut(int x,int y) {
		newroot(x);
		access(y);
		splay(y);
		if(ch[y][0]==x) {
			ch[y][0]=p[x]=0;
			upd(y);
		}
	}
	
	void lik(int x,int y,int i) {
		if(find_root(x)==find_root(y)) {
			newroot(x); 
			access(y);
			splay(y); int z=mi[y];
			pr[i]=z;
			cut(z,ex[z]); cut(z,ey[z]);
		}
		lik(x,i); lik(i,y); 
	}
}L;

struct sgtree {
	int rt[N],tot,ch[N*20][2],sg[N*20];
	void upd(int &x,int last,int l,int r,int pos) {
		x=++tot;
		sg[x]=sg[last]+1;
		lc=ch[last][0];
		rc=ch[last][1];
		if(l==r) return;
		if(pos<=mid) upd(lc,ch[last][0],l,mid,pos);
		else upd(rc,ch[last][1],mid+1,r,pos);
	}
	
	int qry(int x,int l,int r,int ql,int qr) {
		if(!x) return 0;
		if(l>=ql&&r<=qr) return sg[x];
		if(qr<=mid) return qry(lc,l,mid,ql,qr);
		if(ql>mid) return qry(rc,mid+1,r,ql,qr);
		return qry(lc,l,mid,ql,qr)+qry(rc,mid+1,r,ql,qr);
	}
}S;

int main() {
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
	read(n); read(m); read(q); read(type);
	For(i,1,n+m) L.upd(i);
	For(i,1,m) {
		int x,y;
		read(x); read(y);
		ex[i]=x+m; ey[i]=y+m;
		if(x==y) pr[i]=m;
		else L.lik(m+x,m+y,i);
	}
	For(i,1,m) S.upd(S.rt[i],S.rt[i-1],1,m+1,pr[i]+1);
	int ans=0;
	For(i,1,q) {
		int l,r;
		read(l); read(r);
		if(type) { l^=ans; r^=ans; }
		ans=S.qry(S.rt[r],1,m+1,1,l)-S.qry(S.rt[l-1],1,m+1,1,l);
		ans=n-ans;
		printf("%d\n",ans);
	}
	Formylove;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值