[bzoj3514]Codechef MARCH14 GERALD07加强版

感觉这题是真的神,,太瓜了根本想不到。

N个点M条边的无向图,询问保留图中编号在$[l,r]$的边的时候图中的联通块个数。


 

考虑每条边的贡献。一条边可以使联通块数量-1当且仅当加入这条边之后,不形成环即连接了两个联通块。

后加入的边如果形成了环呢?那么只要环中最早加入的边还在,就不会做出贡献。

那么我们按编号从小到大加边,记录对于每条边,加入之后,如果形成环,形成的环中最早也就是编号最小的边是多少;否则就是0。记为$ntr[i]$。(???wtf?)

也就是说只有询问的$l>ntr[i]$时,边$i$才能做出贡献(当然也要$i \leq r$)。

$ntr[i]$可以用LCT维护。(维护边权,就是把边变成点做就行了。装逼的做法嘛,jcy大爷有写过博客)

询问区间有多少$ntr[i]<l$的可以用主席树维护。

然后我是傻逼。LCT在access时候忘了pullup。rotate时候先更新的x,再更新的原父亲。蠢死了。

 

#include<bits/stdc++.h>
using namespace std;
const int N=400010;
inline int read(){
    int r=0,c=getchar();
    while(!isdigit(c))c=getchar();
    while(isdigit(c))
    r=r*10+c-'0',c=getchar();
    return r;
}
int n,m,k,f;
/****NTR****/
#define ls ch[x][0]
#define rs ch[x][1]
int ch[N][2],fa[N],tot;
int rev[N],mn[N],w[N],a[N];
void pd(int x){
    if(rev[x]){
        rev[ls]^=1,rev[rs]^=1;
        rev[x]=0;swap(ls,rs);
    }
}
void pp(int x){
    mn[x]=w[x];
    if(ls)mn[x]=min(mn[x],mn[ls]);
    if(rs)mn[x]=min(mn[x],mn[rs]);
}
int isroot(int x){
    return x!=ch[fa[x]][0]&&x!=ch[fa[x]][1];
}
int get(int x){
    return x==ch[fa[x]][1];
}
void rotate(int x){
    int y=fa[x],z=fa[y],px=get(x),py=get(y);
    int t=ch[x][px^1];
    if(!isroot(y))ch[z][py]=x;fa[x]=z;
    ch[x][px^1]=y,fa[y]=x;
    ch[y][px]=t,fa[t]=y;
    pp(y);pp(x);
}
int s[N],top;
void splay(int x){
    top=0;s[++top]=x;
    for(int i=x;!isroot(i);i=fa[i])s[++top]=fa[i];
    for(int i=top;i;i--)pd(s[i]);
    while(!isroot(x)){
        int y=fa[x];
        if(!isroot(y))
        rotate(get(x)==get(y)?y:x);
        rotate(x);
    }
}
void access(int x){
    for(int las=0;x;las=x,x=fa[x]){
        splay(x);rs=las;pp(x);
    }
}
void makeroot(int x){
    access(x);splay(x);rev[x]^=1;
}
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;
}
int find(int x){
    access(x),splay(x);
    while(ls)x=ls;
    return x;
}
int getmin(int x,int y){
    makeroot(x);access(y);splay(y);
    return mn[y];
}
int p[N],q[N];
void init(){
    n=read(),m=read(),k=read(),f=read();
    for(int i=1;i<=n;i++)w[i]=mn[i]=1e9;
    for(int i=1;i<=m;i++)p[i]=read(),q[i]=read();
    for(int i=1;i<=m;i++){
        w[i+n]=mn[i+n]=i;
        int u=p[i],v=q[i];
        if(u==v){
            a[i]=-1;
            continue;
        }
        if(find(u)==find(v)){
            int w=getmin(u,v);
            cut(p[w],w+n);
            cut(w+n,q[w]);
            a[i]=w;
        }
        link(u,i+n);link(i+n,v);
    }
}
#undef ls
#undef rs
/****hjt***/
int rt[N],ls[N*40],rs[N*40],sum[N*40],cnt;
void ins(int &x,int o,int l,int r,int w){
    x=++cnt;
    ls[x]=ls[o],rs[x]=rs[o];sum[x]=sum[o]+1;
    if(l==r)return;
    int mid=l+r>>1;
    if(w<=mid)ins(ls[x],ls[o],l,mid,w);
    else ins(rs[x],rs[o],mid+1,r,w);
}
int query(int p,int q,int l,int r,int v){
    if(r<=v)return sum[q]-sum[p];
    if(l>v)return 0;
    int mid=l+r>>1,ret=query(ls[p],ls[q],l,mid,v);
    if(v>mid)ret+=query(rs[p],rs[q],mid+1,r,v);
    return ret;
}
void solve(){
    for(int i=1;i<=m;i++)
    ins(rt[i],rt[i-1],0,m,~a[i]?a[i]:m);
    int ans=0;
    while(k--){
        int l=ans^read(),r=ans^read();
        ans=query(rt[l-1],rt[r],0,m,l-1);
        ans=n-ans;
        printf("%d\n",ans);if(!f)ans=0;
    }
}
/****main****/
int main(){
    init();
    solve();
}

 

posted @ 2018-03-02 11:01  orzzz  阅读(208)  评论(0编辑  收藏  举报