[BZOJ3514] Codechef MARCH14 GERALD07加强版

给定 \(n\) 个点 \(m\) 条边的无向图,每次询问保留图中编号在 \([l,r]\) 的边的时候图中联通块个数。强制在线。

\(n,m\leq 2\cdot 10^5\)

Solution

先考虑一下假如可以离线的话怎么做。

把询问按照右端点排序,维护边权 最大生成树,这里的"边权"指的是边的标号。

每次尝试加边,如果这条边连接的两条边不连通就直接加,否则从环上找一条边权最小的边替换,然后用线段树维护当前用了哪条边,如果要加入就对应位置 \(+1\),要删除就对应位置 \(-1\) 。维护生成树的过程用 \(LCT\) 实现。这样对于询问 \([l,r]\) 就查一下在线段树上 \([l,r]\) 的权值和 \(sum\) 表示当前在图上用到了 \(sum\) 条边,\(n-sum\) 即为答案。

然后考虑在线做法。

把线段树换成主席树就好了。

大概扫了一下网上的题解好像做法都是搞出来一个 \(ntr[i]\) 表示加入第 \(i\) 条边的时候替换掉了哪条边,如果没有替换掉边则值为 \(0\)。然后每次询问的时候找主席树 \([0,l-1]\)\(sum\) 就好了。。。然而我并不能想明白为什么是这样,只能用我那种有加有减的做法了...

Code #include<bits/stdc++.h> using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define all(A) A.begin(),A.end() #define mp(A,B) std::make_pair(A,B) const int N=4e5+5; #define ls ch[x][0] #define rs ch[x][1] int ch[N][2],fa[N],mn[N],stk[N],father[N]; int n,m,k,type,val[N],idx[N],rev[N],top,rt[N]; struct Edge{ int x,y; }edge[N]; void pushup(int x){ mn[x]=min(mn[ls],min(mn[rs],val[x])); } void pushr(int x){ swap(ls,rs);rev[x]^=1; } void pushdown(int x){ if(rev[x]) pushr(ls),pushr(rs),rev[x]=0; } bool nroot(int x){ return ch[fa[x]][0]==x or ch[fa[x]][1]==x; } int find(int x){ return father[x]==x?x:father[x]=find(father[x]); } void rotate(int x){ int y=fa[x],z=fa[y],d=ch[y][1]==x,dd=ch[z][1]==y; ch[y][d]=ch[x][d^1];if(ch[x][d^1]) fa[ch[x][d^1]]=y; fa[x]=z;if(nroot(y)) ch[z][dd]=x; ch[x][d^1]=y;fa[y]=x;pushup(y); } void splay(int x){ int now=x;stk[++top]=now; while(nroot(now)) now=fa[now],stk[++top]=now; while(top) pushdown(stk[top--]); while(nroot(x)){ int y=fa[x],z=fa[y]; if(nroot(y)) rotate((ch[y][1]==x)^(ch[z][1]==y)?x:y); rotate(x); } pushup(x); } void access(int x){ for(int y=0;x;y=x,x=fa[x]) splay(x),rs=y;pushup(x); } void makeroot(int x){ access(x),splay(x),pushr(x); } void link(int x,int y){ makeroot(x);fa[x]=y; } void cut(int x,int y){ makeroot(x),access(y),splay(y); fa[x]=ch[y][0]=0;pushup(y); } void split(int x,int y){ makeroot(x),access(y),splay(y); } int getint(){ int X=0,w=0;char ch=getchar(); while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } namespace hjt{ int tot,ch[N*40][2],sum[N*40]; void update(int &x,int las,int l,int r,int ql,int qr,int c){ x=++tot; sum[x]=sum[las]+c;ls=ch[las][0];rs=ch[las][1]; if(l==r) return; int mid=l+r>>1; ql<=mid?update(ls,ch[las][0],l,mid,ql,qr,c):update(rs,ch[las][1],mid+1,r,ql,qr,c); } int query(int las,int x,int l,int r,int ql,int qr){ if(ql<=l and r<=qr) return sum[x]-sum[las]; int mid=l+r>>1,ans=0; if(ql<=mid) ans+=query(ch[las][0],ls,l,mid,ql,qr); if(mid<qr) ans+=query(ch[las][1],rs,mid+1,r,ql,qr); return ans; } }; signed main(){ mn[0]=1e9; n=getint(),m=getint(),k=getint(),type=getint(); for(int i=1;i<=m;i++){ int x=getint(),y=getint(); edge[i]=(Edge){x,y};val[i+n]=i; } for(int i=0;i<=n;i++) father[i]=i,val[i]=1e9; for(int i=1;i<=m;i++){ rt[i]=rt[i-1]; int r1=find(edge[i].x),r2=find(edge[i].y); if(r1!=r2){ hjt::update(rt[i],rt[i],0,m,i,i,1); father[r1]=r2; } else{ if(edge[i].x==edge[i].y) continue; split(edge[i].x,edge[i].y); int xx=mn[edge[i].y]; hjt::update(rt[i],rt[i],0,m,xx,xx,-1); hjt::update(rt[i],rt[i],0,m,i,i,1); cut(edge[xx].x,xx+n),cut(edge[xx].y,xx+n); } link(edge[i].x,i+n),link(edge[i].y,i+n); } int lasans=0; while(k--){ int l=getint(),r=getint(); if(type) l^=lasans,r^=lasans; int p=hjt::query(rt[l-1],rt[r],0,m,l,r); printf("%d\n",lasans=n-p); } return 0; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpppfz.html