bzoj3514Codechef MARCH14 GERALD07加强版
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define maxn 400005 #define maxm 200005 #define maxk 4000005 using namespace std; int n,m,k,type,ans,Ans,size,fa[maxn],son[maxn][2],sm[maxn],Val[maxm],val[maxn],sum[maxk],root[maxm],lc[maxk],rc[maxk]; bool rev[maxn]; struct date{ int u,v; }wi[maxm]; struct note{ bool which(int x){ return son[fa[x]][1]==x; } bool isroot(int x){ return son[fa[x]][0]!=x&&son[fa[x]][1]!=x; } void update(int x){ sm[x]=val[x]; if (son[x][0]) sm[x]=min(sm[x],sm[son[x][0]]); if (son[x][1]) sm[x]=min(sm[x],sm[son[x][1]]); } void pushdown(int x){ if (!rev[x]) return; rev[x]^=1,swap(son[x][0],son[x][1]); if (son[x][0]) rev[son[x][0]]^=1; if (son[x][1]) rev[son[x][1]]^=1; } void relax(int x){ if (!isroot(x)) relax(fa[x]); pushdown(x); } void rotata(int x){ int y=fa[x],d=which(x),dd=which(y); if (!isroot(y)) son[fa[y]][dd]=x; fa[x]=fa[y]; fa[son[x][d^1]]=y,son[y][d]=son[x][d^1]; fa[y]=x,son[x][d^1]=y; update(y); } void splay(int x){ relax(x); while (!isroot(x)){ if (isroot(fa[x])) rotata(x); else if (which(x)==which(fa[x])) rotata(fa[x]),rotata(x); else rotata(x),rotata(x); } update(x); } void access(int x){ for (int p=0;x;x=fa[x]){ splay(x); son[x][1]=p; p=x; update(x); } } void make_root(int x){ access(x); splay(x); rev[x]^=1; } void link(int x,int y){ make_root(x); fa[x]=y; } void cut(int x,int y){ make_root(x); access(y); splay(y); son[y][0]=fa[x]=0; update(y); } void split(int x,int y){ make_root(x); access(y); splay(y); } int find_root(int x){ access(x); splay(x); while (son[x][0]) x=son[x][0]; return x; } int find(int x,int y){ split(x,y); return sm[y]; } }lct; void insert(int &k,int p,int l,int r,int x){ k=++size,sum[k]=sum[p]+1; int mid=(l+r)/2; if (l==r) return; if (x<=mid) rc[k]=rc[p],insert(lc[k],lc[p],l,mid,x); else lc[k]=lc[p],insert(rc[k],rc[p],mid+1,r,x); } void insert(int id,int x){ insert(root[id],root[id-1],0,m+1,x); } void Query(int k1,int k2,int l,int r,int x,int y){ if (!k1&&!k2) return; if (l>=x&&r<=y){ Ans+=(sum[k2]-sum[k1]); return; } int mid=(l+r)/2; if (x<=mid) Query(lc[k1],lc[k2],l,mid,x,y); if (y>mid) Query(rc[k1],rc[k2],mid+1,r,x,y); } int query(int x,int y){ Ans=0; int u=root[x-1],v=root[y],l=0,r=m+1,mid; Query(u,v,l,r,0,x-1); return Ans; } int main(){ int u,v,t1,t2,temp; scanf("%d%d%d%d",&n,&m,&k,&type); memset(sum,0,sizeof(sum)); memset(Val,0,sizeof(Val)); memset(fa,0,sizeof(fa)); memset(son,0,sizeof(son)); memset(rev,0,sizeof(rev)); for (int i=1;i<=m;i++) scanf("%d%d",&wi[i].u,&wi[i].v); for (int i=1;i<=n;i++) val[i]=sm[i]=m+1; for (int i=1;i<=m;i++) val[n+i]=sm[n+i]=i; for (int i=1;i<=m;i++){ u=wi[i].u,v=wi[i].v; if (u==v) Val[i]=m+1; if (u==v) continue; if (lct.find_root(u)!=lct.find_root(v)){ Val[i]=0; lct.link(n+i,u),lct.link(n+i,v); }else{ temp=lct.find(u,v); Val[i]=temp,temp+=n; lct.cut(temp,wi[temp-n].u),lct.cut(temp,wi[temp-n].v); lct.link(n+i,u),lct.link(n+i,v); } } memset(root,0,sizeof(root)); for (int i=1;i<=m;i++){ insert(i,Val[i]); } ans=0; for (int i=1;i<=k;i++){ scanf("%d%d",&u,&v); if (type==1) u^=ans,v^=ans; if (u>v) swap(u,v); ans=n-query(u,v); printf("%d\n",ans); } return 0; }
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3514
题目大意:N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。
做法:如果要求整个图中联通块的个数,记加入该边后不形成环的边数记为x,则答案为n-x,这题问的是加入编号为[l~r]中的边后联通块的个数,答案便稍微变化了一下,记能使联通块数目-1的边集为{S},答案为n-|S|,问题便成为了:有多少条边属于{S},仔细想想,可以先预处理一个数组val[i],怎么预处理呢?可以按编号依次加入边,有两种情况:*1.如果加入的这条边不形成环,则这条边的权值赋值为0,并加入这条边;*2.加入这条边形成环,记环上编号最小的边的编号为y,这加入的这条边权值为y,并删去编号最小的那条边,加入该边。这个过程用lct模拟即可,比较基础的操作。
得出每条边的权值val[i]后,对于一个询问[L~R],如果一条边的val<l,则该边属于{S},问题便简化为一个数组val,问在区间L~R中权值在0~L-1的个数,显而易见,可持久化线段树轻松搞定。问题得以圆满解决。(Lct+可持久化线段树)