【BZOJ-3514】Codechef MARCH14 GERALD07加强版 LinkCutTree + 主席树
3514: Codechef MARCH14 GERALD07加强版
Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 1288 Solved: 490
[Submit][Status][Discuss]
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
1 3
1 2
2 1
3 2
2 2
2 3
1 5
5 5
1 2
Sample Output
2
1
3
1
1
3
1
HINT
对于100%的数据,1≤N、M、K≤200,000。
2016.2.26提高时限至60s
Source
Solution
这应该算是动态图问题吧?? 问了一下ShallWe,用LCT维护动态图问题的一种离线做法是维护一颗 时间最大生成树 ,所以这个也是一样。
思路非常的巧妙,首先维护一颗 时间最大生成树 ,按时间顺序加边。
设当前加边为$<u,v>$,如果$u$和$v$属于同一个联通块,则加入$<u,v>$必然会形成环,那么切掉这个环上的边权(时间)最小的边,连上这个边,记这个被切掉的边为$pop_{i}$
然后这个题要求联通块的个数,然后发现,对于$pop$存在一个性质:
对于一条边$x$,在边$x$和使边$x$被切的边$y$之间连上的边,是不会与使边$x$被切得边$y$出现环的,即如果$r>y>l>x$则$y$必然会使联通块-1
所以问题就可以转化为求$[l,r]$中$pop<l$的$pop$的个数,这个可以用主席树去维护,所以答案就是$N-sum$。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; #define INF 0x3fffffff #define MAXN 400010 inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } int N,M,K,T,last; struct EdgeNode{int u,v;}edge[MAXN]; namespace LCT { int fa[MAXN],son[MAXN][2],id[MAXN],tim[MAXN]; bool rev[MAXN]; inline void Init() {for (int i=1; i<=N; i++) id[i]=i,tim[i]=INF;} inline bool is_root(int x) {return !fa[x] || son[fa[x]][0]!=x&&son[fa[x]][1]!=x;} inline int Min(int x,int y) {return tim[x]<tim[y]? x:y;} inline void Update(int x) { id[x]=x; if (son[x][0]) id[x]=Min(id[x],id[son[x][0]]); if (son[x][1]) id[x]=Min(id[x],id[son[x][1]]); } inline void Rev(int x) {if (!x) return; swap(son[x][0],son[x][1]),rev[x]^=1;} inline void Pushdown(int x) { if (!x) return; if (rev[x]) Rev(son[x][0]),Rev(son[x][1]),rev[x]^=1; } inline void Rotate(int x) { int y=fa[x],w=son[y][1]==x,z=fa[y]; son[y][w]=son[x][w^1]; if (son[x][w^1]) fa[son[x][w^1]]=y; if (son[z][0]==y) son[z][0]=x; else if (son[z][1]==y) son[z][1]=x; fa[x]=z; fa[y]=x; son[x][w^1]=y; Update(y); } int stack[MAXN]; inline void Splay(int x) { int t=x,top=0,y; stack[++top]=x; while (!is_root(t)) stack[++top]=t=fa[t]; while (top) Pushdown(stack[top--]); while (!is_root(x)) { y=fa[x]; if (!is_root(y)) if ((son[fa[y]][0]==y)^(son[y][0]==x)) Rotate(x); else Rotate(y); Rotate(x); } Update(x); } inline void Access(int x) {for (int y=0; x; y=x,x=fa[x]) Splay(x),son[x][1]=y,Update(x);} inline void Makeroot(int x) {Access(x); Splay(x); Rev(x);} inline void Link(int x,int y) {Makeroot(x); fa[x]=y; Access(x);} inline void Cut(int x) {Access(x); Splay(x); fa[son[x][0]]=0; son[x][0]=0; Update(x);} inline void Cut(int x,int y) {Makeroot(x); Access(y); Cut(y);} inline int Find(int x) {Access(x); Splay(x); while (son[x][0]) x=son[x][0]; return x;} inline int Query(int x,int y) {Makeroot(x); Access(y); Splay(y); return id[y];} }using namespace LCT; namespace PrTree { int root[MAXN],lson[MAXN*20],rson[MAXN*20],sum[MAXN*20],sz; inline void Insert(int l,int r,int &now,int par,int val) { now=++sz; sum[now]=sum[par]+1; if (l==r) {return;} lson[now]=lson[par],rson[now]=rson[par]; int mid=(l+r)>>1; if (val<=mid) Insert(l,mid,lson[now],lson[par],val); else Insert(mid+1,r,rson[now],rson[par],val); } inline int Query(int l,int r,int L,int R,int val) { if (r<=val) return sum[R]-sum[L]; int mid=(l+r)>>1; if (val<=mid) return Query(l,mid,lson[L],lson[R],val); else return Query(l,mid,lson[L],lson[R],val)+Query(mid+1,r,rson[L],rson[R],val); } }using namespace PrTree; int pop[MAXN]; int main() { N=read(),M=read(),K=read(),T=read(); for (int i=1; i<=M; i++) edge[i].u=read(),edge[i].v=read(); LCT::Init(); for (int i=1; i<=M; i++) { int u=edge[i].u,v=edge[i].v; if (u==v) {pop[i]=i; continue;} if (LCT::Find(u)==LCT::Find(v)) { pop[i]=LCT::Query(u,v); LCT::Cut(u,pop[i]); LCT::Cut(v,pop[i]); pop[i]-=N; } id[i+N]=i+N,tim[i+N]=i; LCT::Link(u,i+N); LCT::Link(v,i+N); } // for (int i=1; i<=M; i++) printf("%d ",pop[i]); puts(""); for (int i=1; i<=M; i++) PrTree::Insert(0,M,root[i],root[i-1],pop[i]); while (K--) { int L=read(),R=read(); if (T) L^=last,R^=last; printf("%d\n",last=N-Query(0,M,root[L-1],root[R],L-1)); } return 0; }
——It's a lonely path. Don't make it any lonelier than it has to be.