BZOJ 3514: Codechef MARCH14 GERALD07加强版(LCT+主席树)

130 篇文章 1 订阅
124 篇文章 0 订阅

题目
一个直观的想法:如果题目保证随时是一颗树就好了。
那么答案就 = n - (R - L + 1)
一个合理的联想:我可不可以通过对边的统计得到答案?
如果我们把 [ L , R ] [L,R] [L,R]中的边拿出来做克鲁斯卡尔,边权为边编号。
那么一条边联通两个联通块,使联通块数量减小,当且仅当它是第一条连接这两个联通块的。
像:区间查询颜色种类,每个颜色第一次出现才会有贡献。
那么我们求出来每条边 u u u可以替换的编号最小的边 v v v
那么如果这条边 v v v [ L , R ] [L,R] [L,R]中, u u u就没有贡献。
反之若 v v v [ 0 , L − 1 ] [0,L-1] [0,L1]中( v v v可以不存在,记为0), u u u [ L , R ] [L,R] [L,R]中, u u u可以使联通块数量-1.
统计有多少对这样的 u , v u,v u,v即可。
v v v可以用LCT,每次取出形成的环中的最小边。
统计可以用主席树。
O ( n log ⁡ n ) O(n\log n) O(nlogn)
AC Code:

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

int n,m,K,type,cnt;
int bl[maxn][2];
vector<int>G[maxn];
namespace LCT{
	int ch[maxn][2],fa[maxn],Min[maxn],val[maxn],rev[maxn];
	#define il inline 
	#define pa fa[x]
	il int inr(int x){ return ch[pa][1]==x; }
	il int isr(int x){ return ch[pa][0]!=x && ch[pa][1]!=x; }
	il void dt(int x){
		if(rev[x]){
			swap(ch[x][0],ch[x][1]),rev[x]=0;
			if(ch[x][0]) rev[ch[x][0]]^=1;
			if(ch[x][1]) rev[ch[x][1]]^=1;
		}
	}
	il void dtpath(int x){
		if(!isr(x)) dtpath(pa);
		dt(x);
	}
	il void upd(int x){
		Min[x]=x;
		if(val[Min[ch[x][0]]]<val[Min[x]]) Min[x] = Min[ch[x][0]];
		if(val[Min[ch[x][1]]]<val[Min[x]]) Min[x] = Min[ch[x][1]];
	}
	il void rot(int x){
		int y = fa[x] , z = fa[y] , c = inr(x);
		if(!isr(y)) ch[z][inr(y)] = x;
		(ch[y][c]=ch[x][!c])&&(fa[ch[y][c]]=y);
		fa[fa[ch[x][!c]=y]=x]=z;
		upd(y),upd(x);
	}
	il void splay(int x){
		for(dtpath(x);!isr(x);rot(x))
			if(!isr(pa)) rot(inr(pa)==inr(x)?pa:x);
	}
	il int access(int x,int y=0){
		for(;x;x=fa[y=x]) 
			splay(x),ch[x][1]=y,upd(x);
		return y;
	}
	il void bert(int x){
		access(x),splay(x),rev[x]^=1;
	}
	il int sert(int x){
		access(x),splay(x);
		for(;ch[x][0];x=ch[x][0]);
		return x;
	}
	il void link(int x,int y){
		bert(x),fa[x]=y;
	}
	il void cut(int x,int y){
		bert(x),access(y),splay(y);
		fa[x]=ch[y][0]=0;
		upd(y);
	}
	il int query(int x,int y){
		bert(x),access(y),splay(y);
		return Min[y];
	}
	il void insert(int x,int y,int w){
		if(sert(x)!=sert(y)){
			val[++cnt]=w,Min[cnt]=cnt;
			link(bl[cnt][0]=x,cnt),link(bl[cnt][1]=y,cnt);
			G[0].push_back(w);
			return;
		}
		//printf("@%d %d\n",x,y);
		int tmp = query(x,y);
		//printf("%d\n",tmp);
		G[val[tmp]].push_back(w);
		cut(tmp,bl[tmp][0]),cut(tmp,bl[tmp][1]);
		val[tmp] = w , Min[tmp] = tmp;
		link(tmp,bl[tmp][0]=x),link(tmp,bl[tmp][1]=y);
	}
}

int rt[maxn],lc[maxn*30],rc[maxn*30],siz[maxn*30],tot;
void Insert(int &now,int l,int r,int pos){
	siz[++tot] = siz[now] + 1, lc[tot] = lc[now] , rc[tot] = rc[now];
	now = tot;
	if(l == r) return;
	int mid = (l+r) >> 1;
	if(pos <= mid) Insert(lc[now],l,mid,pos);
	else Insert(rc[now],mid+1,r,pos);
}

int query(int now,int l,int r,int ql,int qr){
	if(!now||ql>r||l>qr) return 0;
	if(ql<=l && r<=qr) return siz[now];
	int mid = (l+r) >> 1;
	return query(lc[now],l,mid,ql,qr)+query(rc[now],mid+1,r,ql,qr);
}

int main(){
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	LCT::val[0]=0x7f7f7f7f;
	scanf("%d%d%d%d",&n,&m,&K,&type),cnt=n;
	for(int i=1;i<=n;i++) LCT::Min[i]=i,LCT::val[i]=0x3f3f3f3f;
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		if(u!=v)
			LCT::insert(u,v,i);
	}
	//puts("1");
	for(int i=0;i<m;i++){
		if(i)rt[i] = rt[i-1];
		for(int j=0,siz=G[i].size();j<siz;j++)
			Insert(rt[i],1,m,G[i][j]);
	}
	int la = 0;
	for(int L,R;K--;){
		scanf("%d%d",&L,&R);
		if(type)L^=la , R^=la;
		printf("%d\n",la=(n-query(rt[L-1],1,m,L,R)));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值