【刷题】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;
}