BZOJ3514: Codechef MARCH14 GERALD07加强版
BZOJ3514: 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
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
题解Here!
首先把边依次加到图中,若当前这条边与图中的边形成了环,那么把这个环中最早加进来的边弹出去,并将每条边把哪条边弹了出去记录下来:add_time[i] = j;
特别地,要是没有弹出边,add_time[i] = 0;
这个显然是可以用LCT来弄的对吧。
然后对于每个询问,我们的答案就是对l~r中add_time小于l的边求和,并用n减去这个值。
对于查询从l ~ r中有多少边的add_time小于l,我用的是主席树。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #define MAXN 400010 #define MAX (1<<30) using namespace std; int n,m,q; int val[MAXN],root[MAXN],add_time[MAXN]; bool k; struct Graph{ int x,y; }a[MAXN]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } namespace LCT{ int top=0,stack[MAXN]; struct Link_Cut_Tree{ int son[2]; int f,v,flag; }a[MAXN]; inline bool isroot(int rt){ return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt; } inline void pushup(int rt){ if(!rt)return; a[rt].v=rt; if(val[a[rt].v]>val[a[a[rt].son[0]].v])a[rt].v=a[a[rt].son[0]].v; if(val[a[rt].v]>val[a[a[rt].son[1]].v])a[rt].v=a[a[rt].son[1]].v; } inline void pushdown(int rt){ if(!rt||!a[rt].flag)return; a[a[rt].son[0]].flag^=1;a[a[rt].son[1]].flag^=1;a[rt].flag^=1; swap(a[rt].son[0],a[rt].son[1]); } inline void turn(int rt){ int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0; if(!isroot(x)){ if(a[y].son[0]==x)a[y].son[0]=rt; else a[y].son[1]=rt; } a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x; a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x; pushup(x);pushup(rt); } void splay(int rt){ top=0; stack[++top]=rt; for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f; while(top)pushdown(stack[top--]); while(!isroot(rt)){ int x=a[rt].f,y=a[x].f; if(!isroot(x)){ if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt); else turn(x); } turn(rt); } } void access(int rt){ for(int i=0;rt;i=rt,rt=a[rt].f){ splay(rt); a[rt].son[1]=i; pushup(rt); } } inline void makeroot(int rt){access(rt);splay(rt);a[rt].flag^=1;} int find(int rt){ access(rt);splay(rt); while(a[rt].son[0])rt=a[rt].son[0]; return rt; } inline void split(int x,int y){makeroot(x);access(y);splay(y);} inline void link(int x,int y){makeroot(x);a[x].f=y;} inline void cut(int x,int y){split(x,y);a[x].f=a[y].son[0]=0;} inline int query(int x,int y){split(x,y);return a[y].v;} } namespace CT{ int size=1; struct Charman_Tree{ int l,r,sum; }a[MAXN*40]; inline void buildtree(){ root[0]=0; a[0].l=a[0].r=a[0].sum=0; } void insert(int k,int l,int r,int &rt){ a[size]=a[rt];rt=size++; a[rt].sum++; if(l==r)return; int mid=l+r>>1; if(k<=mid)insert(k,l,mid,a[rt].l); else insert(k,mid+1,r,a[rt].r); } int query(int i,int j,int l,int r,int k){ if(r==k)return a[j].sum-a[i].sum; int mid=l+r>>1,t=a[a[j].l].sum-a[a[i].l].sum; if(k<=mid)return query(a[i].l,a[j].l,l,mid,k); else return t+query(a[i].r,a[j].r,mid+1,r,k); } } void work(){ int l,r,last=0; while(q--){ l=read();r=read(); if(k){l^=last;r^=last;} last=n-CT::query(root[l-1],root[r],0,m,l-1); printf("%d\n",last); } } void init(){ int x,y; n=read();m=read();q=read();k=read(); val[0]=MAX; for(int i=1;i<=n;i++){ val[i]=MAX; LCT::a[i].v=i; } for(int i=1;i<=m;i++){a[i].x=read();a[i].y=read();} for(int i=1;i<=m;i++){ x=a[i].x;y=a[i].y; if(x==y){ add_time[i]=i; continue; } if(LCT::find(x)==LCT::find(y)){ int id=LCT::query(x,y),w=val[id]; add_time[i]=w; LCT::cut(a[w].x,id); LCT::cut(a[w].y,id); } val[i+n]=i; LCT::a[i+n].v=i+n; LCT::link(x,i+n); LCT::link(y,i+n); } CT::buildtree(); for(int i=1;i<=m;i++){ root[i]=root[i-1]; CT::insert(add_time[i],0,m,root[i]); } } int main(){ init(); work(); return 0; }