1. 程式人生 > >BZOJ3514 GERALD07加強版

BZOJ3514 GERALD07加強版

Link

Difficulty

演算法難度7,思維難度7,程式碼難度7

Description

給定一個無向圖,有nn個點,mm條邊,邊從11mm標號

qq次詢問,每次詢問[l,r][l,r]內的邊組成的圖的連通塊個數

1n,m,q2×1051\le n,m,q \le 2\times 10^5

Solution

這題聽說可以樹套樹,但是會TLE

以下都是神仙做法:

我們維護按照編號加邊,維護邊的最大生成樹,邊的權值就是編號

每次如果沒有形成環就直接加,如果有環就替換掉最小的那條邊,可以LCT維護

我們記第ii條邊替換的邊的序號為ntrintr_i

,特別的,自環的ntr=mntr=m,直接加的邊的ntr=0ntr=0

查詢的時候直接查[l,r][l,r]ntri<lntr_i<l的個數,答案就是nn減去這個個數,可以主席樹維護

接下來我們嘗試證明這個結論的正確性:

我們考慮如果一條位於[l,r][l,r]內的邊的ntrntr取不同的值時,它對答案的貢獻

  1. ntri=0ntr_i=0時,也就是可以直接加上這條邊,那麼連通塊個數會-1

  2. 0<ntri<l0<ntr_i<l

    ntri<l時,也就是它替換掉了前面出現過的一條邊,並且這條邊的編號&lt;l&lt;l

    你可能會說:“它沒有貢獻啊,它明明替換了一條邊,怎麼會有貢獻呢?”

    但是你會發現它替換的邊的編號&lt;l&lt;l,那麼這時這條邊已經不存在了

    也就是說現在這條邊就可以替代它的作用了,這時它就可以有1的貢獻了

  3. lntrirl\le ntr_i\le r時,也就是說它替換了一條前面出現的邊,並且這條邊的編號l\ge l

    和前面同理,只不過這時它替換的邊是存在的,那麼現在這條邊就沒有貢獻了

  4. ntri=mntr_i=m

    時,也就是說這條邊是一個自環,那麼它一定不會有貢獻

這樣我們就求得了[l,r][l,r]內的邊對答案的貢獻了,就可以計算答案了

時間複雜度O(nlogn)O(nlogn),本人在bzoj上跑了32s,寫起來應該沒什麼常數問題

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#define LL long long
using namespace std;
inline int read(){
    int x=0,f=1;char ch=' ';
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return f==1?x:-x;
}
const int N=4e5+5;
namespace LCT{
    int ch[N][2],f[N],rev[N],mn[N],id[N];
    inline bool get(int x){return ch[f[x]][1]==x;}
    inline void pushup(int x){mn[x]=min(id[x],min(mn[ch[x][0]],mn[ch[x][1]]));}
    inline bool nroot(int x){return ch[f[x]][0]==x || ch[f[x]][1]==x;}
    inline void reverse(int x){rev[x]^=1;swap(ch[x][0],ch[x][1]);}
    inline void pushdown(int x){
        if(rev[x]){
            if(ch[x][0])reverse(ch[x][0]);
            if(ch[x][1])reverse(ch[x][1]);
            rev[x]=0;
        }
    }
    inline void pd(int x){if(nroot(x))pd(f[x]);pushdown(x);}
    inline void rotate(int x){
        int y=f[x],z=f[y],w=get(x);
        f[x]=z;if(nroot(y))ch[z][get(y)]=x;
        ch[y][w]=ch[x][w^1];f[ch[y][w]]=y;
        ch[x][w^1]=y;f[y]=x;
        pushup(y);pushup(x);
    }
    inline void Splay(int x){
        pd(x);
        for(int fa=f[x];nroot(x);rotate(x),fa=f[x])
            if(nroot(fa))
                rotate((get(fa)==get(x))?fa:x);
    }
    inline void access(int x){for(int y=0;x;x=f[y=x])Splay(x),ch[x][1]=y,pushup(x);}
    inline void makeroot(int x){access(x);Splay(x);reverse(x);}
    inline int findroot(int x){access(x);Splay(x);while(ch[x][0])x=ch[x][0];return x;}
    inline void split(int x,int y){makeroot(x);access(y);Splay(y);}
    inline void link(int x,int y){split(x,y);f[x]=y;}
    inline void cut(int x,int y){split(x,y);f[x]=ch[y][0]=0;}
};
using namespace LCT;
int n,m,q;
int X[N],Y[N],ntr[N];
int sum[N*30],ls[N*30],rs[N*30],sz,root[N];
inline void insert(int &now,int pre,int l,int r,int pos){
    now=++sz;
    sum[now]=sum[pre]+1;
    ls[now]=ls[pre];
    rs[now]=rs[pre];
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pos<=mid)insert(ls[now],ls[pre],l,mid,pos);
    else insert(rs[now],rs[pre],mid+1,r,pos);
}
inline int query(int now,int pre,int l,int r,int pos){
    if(l==r)return sum[now]-sum[pre];
    int mid=(l+r)>>1;
    if(pos<=mid)return query(ls[now],ls[pre],l,mid,pos);
    else return query(rs[now],rs[pre],mid+1,r,pos)+sum[ls[now]]-sum[ls[pre]];
}
int main(){
    memset(mn,0x3f,sizeof mn);
    memset(id,0x3f,sizeof id);
    n=read();m=read();q=read();int type=read();
    for(int i=1;i<=m;++i){
        X[i]=read();
        Y[i]=read();
        mn[i+n]=id[i+n]=i;
        if(X[i]==Y[i]){
            ntr[i]=m;
            insert(root[i],root[i-1],0,m,ntr[i]);
            continue;
        }
        int fx=findroot(X[i]),fy=findroot(Y[i]);
        if(fx!=fy){
            link(i+n,X[i]);
            link(i+n,Y[i]);
            ntr[i]=0;
        }
        else{
            split(X[i],Y[i]);
            int id=mn[Y[i]];
            cut(id+n,X[id]);
            cut(id+n,Y[id]);
            link(i+n,X[i]);
            link(i+n,Y[i]);
            ntr[i]=id;
        }
        insert(root[i],root[i-1],0,m,ntr[i]);
    }
    int lastans=0;
    for(int i=1;i<=q;++i){
        int l=read()^lastans,r=read()^lastans;
        printf("%d\n",lastans=n-query(root[r],root[l-1],0,m,l-1));
        if(!type)lastans=0;
    }
    return 0;
}