【刷题】BZOJ 3514 Codechef MARCH14 GERALD07加强版

Description

N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。

Input

第一行四个整数N、M、K、type,代表点数、边数、询问数以及询问是否加密。

接下来M行,代表图中的每条边。

接下来K行,每行两个整数L、R代表一组询问。对于type=0的测试点,读入的L和R即为询问的L、R;对于type=1的测试点,每组询问的L、R应为L xor lastans和R xor lastans。

Output

K行每行一个整数代表该组询问的联通块个数。

Sample Input

3 5 4 0

1 3

1 2

2 1

3 2

2 2

2 3

1 5

5 5

1 2

Sample Output

2

1

3

1

HINT

对于100%的数据,1≤N、M、K≤200,000。

Solution

题目很神,还是太弱了,想不出来。LCT加主席树

答案的求法:依次考虑每一条边,如果加入这条边 \(i\) 会生成环,那就删除这个环里最早加入的边 \(j\) ,并且记录下来 \(fout[i]=j\),代表 \(i\) 的加入弹掉了 \(j\) 号边

然后发现对于一个询问 \(l,r\),就看在 \(l\)\(r\) 之间有多少条边 \(i\)\(fout[i]<l\),然后用 \(n\) 减掉这个数,就是一次询问的答案

对于方法的解释就是,如果 \(i\) 边的 \(fout\) 小于 \(l\) ,那么如果只存在 \(l\)\(r\) 的边话,\(i\) 边必定会连接上两个联通块,答案就要-1;反之,如果它的 \(fout\) 大于等于 \(l\) ,那么这条边连的是一个联通块里的两个点,不会对答案产生贡献

然后维护 \(fout\) 数组,可以用LCT维护生成树的方法

查询 \(l\)\(r\) 之间有多少 \(fout\) 小于 \(l\) 的,可以用主席树维护

很巧妙,要加强思维啊

#include<bits/stdc++.h>
#define ll long long
#define db double
#define ld long double
const int MAXN=200000+10,MAXM=200000+10,inf=0x3f3f3f3f;
int n,m,k,type,fa[MAXN],fout[MAXM],ans;
struct edge{
	int u,v;
};
edge side[MAXM];
#define lc(x) ch[(x)][0]
#define rc(x) ch[(x)][1]
struct LCT{
	int ch[MAXN+MAXM][2],fa[MAXN+MAXM],rev[MAXN+MAXM],id[MAXN+MAXM],stack[MAXN+MAXM],cnt,Mn[MAXN+MAXM],val[MAXN+MAXM];
	inline void init()
	{
		memset(Mn,inf,sizeof(Mn));
		memset(val,inf,sizeof(val));
	}
	inline bool nroot(int x)
	{
		return lc(fa[x])==x||rc(fa[x])==x;
	}
	inline void reverse(int x)
	{
		std::swap(lc(x),rc(x));
		rev[x]^=1;
	}
	inline void pushup(int x)
	{
		Mn[x]=val[x],id[x]=x;
		if(Mn[lc(x)]<Mn[x])Mn[x]=Mn[lc(x)],id[x]=id[lc(x)];
		if(Mn[rc(x)]<Mn[x])Mn[x]=Mn[rc(x)],id[x]=id[rc(x)];
	}
	inline void pushdown(int x)
	{
		if(rev[x])
		{
			if(lc(x))reverse(lc(x));
			if(rc(x))reverse(rc(x));
			rev[x]=0;
		}
	}
	inline void rotate(int x)
	{
		int f=fa[x],p=fa[f],c=(rc(f)==x);
		if(nroot(f))ch[p][rc(p)==f]=x;
		fa[ch[f][c]=ch[x][c^1]]=f;
		fa[ch[x][c^1]=f]=x;
		fa[x]=p;
		pushup(f);
		pushup(x);
	}
	inline void splay(int x)
	{
		cnt=0;
		stack[++cnt]=x;
		for(register int i=x;nroot(i);i=fa[i])stack[++cnt]=fa[i];
		while(cnt)pushdown(stack[cnt--]);
		for(register int y=fa[x];nroot(x);rotate(x),y=fa[x])
			if(nroot(y))rotate((lc(y)==x)==(lc(fa[y])==y)?y:x);
		pushup(x);
	}
	inline void access(int x)
	{
		for(register int y=0;x;x=fa[y=x])splay(x),rc(x)=y,pushup(x);
	}
	inline int findroot(int x)
	{
		access(x);splay(x);
		while(lc(x))pushdown(x),x=lc(x);
		splay(x);
		return x;
	}
	inline void makeroot(int x)
	{
		access(x);splay(x);reverse(x);
	}
	inline void split(int x,int y)
	{
		makeroot(x);access(y);splay(y);
	}
	inline void link(int x,int y)
	{
		makeroot(x);fa[x]=y;
	}
	inline void cut(int x,int y)
	{
		split(x,y);fa[x]=lc(y)=0;pushup(y);
	}
};
LCT T1;
#undef lc
#undef rc
#define Mid ((l+r)>>1)
#define lson l,Mid
#define rson Mid+1,r
struct ChairMan_Tree{
	int sum[MAXN<<5],lc[MAXN<<5],rc[MAXN<<5],root[MAXN],cnt;
	inline void Build(int &rt,int l,int r)
	{
		rt=++cnt;
		sum[rt]=0;
		if(l==r)return ;
		Build(lc[rt],lson);
		Build(rc[rt],rson);
	}
	inline void Insert(int &rt,int l,int r,int last,int pos)
	{
		rt=++cnt;
		lc[rt]=lc[last];
		rc[rt]=rc[last];
		sum[rt]=sum[last]+1;
		if(l==r)return ;
		else
		{
			if(pos<=Mid)Insert(lc[rt],lson,lc[last],pos);
			else Insert(rc[rt],rson,rc[last],pos);
		}
	}
	inline int Query(int now,int last,int l,int r,int k)
	{
		if(r==k)return sum[now]-sum[last];
		else
		{
			if(k<=Mid)return Query(lc[now],lc[last],lson,k);
			else return sum[lc[now]]-sum[lc[last]]+Query(rc[now],rc[last],rson,k);
		}
	}
};
ChairMan_Tree T2;
#undef Mid
#undef lson
#undef rson
template<typename T> inline void read(T &x)
{
	T data=0,w=1;
	char ch=0;
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
	x=data*w;
}
template<typename T> inline void write(T x,char c='\0')
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
	if(c!='\0')putchar(c);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline int found(int x)
{
	if(fa[x]!=x)fa[x]=found(fa[x]);
	return fa[x];
}
int main()
{
	read(n);read(m);read(k);read(type);
	T1.init();
	for(register int i=1;i<=n;++i)fa[i]=i;
	for(register int i=1;i<=m;++i)
	{
		int u,v,sn=i+n,x,y;
		read(u);read(v);
		side[i].u=u;side[i].v=v;
		if(u==v)
		{
			fout[i]=i;
			continue;
		}
		x=found(u),y=found(v);
		if(x!=y)
		{
			fa[x]=y;
			T1.val[sn]=i;
			T1.link(sn,u);T1.link(sn,v);
		}
		else
		{
			T1.split(u,v);
			int so=T1.id[v];
			fout[i]=so-n;
			T1.cut(so,side[so-n].u);T1.cut(so,side[so-n].v);
			T1.val[sn]=i;
			T1.link(sn,u);T1.link(sn,v);
		}
	}
	T2.Build(T2.root[0],0,m);
	for(register int i=1;i<=m;++i)T2.Insert(T2.root[i],0,m,T2.root[i-1],fout[i]);
	while(k--)
	{
		int l,r;
		read(l);read(r);
		if(type)l^=ans,r^=ans;
		ans=n-T2.Query(T2.root[r],T2.root[l-1],0,m,l-1);
		write(ans,'\n');
	}
	return 0;
}
posted @ 2018-04-05 21:50  HYJ_cnyali  阅读(117)  评论(0编辑  收藏  举报