BZOJ3514 / Codechef GERALD07 Chef and Graph Queries LCT、主席树

传送门——BZOJ

传送门——VJ


考虑使用LCT维护时间最大生成树,那么对于第\(i\)条边,其加入时可能会删去一条边。记\(pre_i\)表示删去的边的编号,如果不存在则\(pre_i = 0\),如果是自环则\(pre_i = i\)

因为连通块数量等于点数减树边数量,而对于一组询问\([l,r]\),当\(pre_i < l \leq i \leq r\)的时候就会在这张图上额外增加一条树边。所以我们只需要使用主席树做一个二维数点就可以了。

时空复杂度\(O(nlogn)\)

#include<bits/stdc++.h>
using namespace std;
 
int read(){
    int a = 0; char c = getchar(); bool f = 0;
    while(!isdigit(c)){f = c == '-'; c = getchar();}
    while(isdigit(c)){
        a = a * 10 + c - 48; c = getchar();
    }
    return f ? -a : a;
}
 
const int _ = 4e5 + 7;
 
namespace segt{
    const int __ = _ * 30;
    int sum[__] , lch[__] , rch[__] , cnt;
 
#define mid ((l + r) >> 1)
     
    void modify(int &x , int l , int r , int tar){
        int t = ++cnt; sum[t] = sum[x] + 1; lch[t] = lch[x]; rch[t] = rch[x]; x = t;
        if(l == r) return;
        mid >= tar ? modify(lch[x] , l , mid , tar) : modify(rch[x] , mid + 1 , r , tar);
    }
 
    int qry(int x , int l , int r , int L , int R){
        if(!x || l >= L && r <= R) return sum[x];
        int sum = 0;
        if(mid >= L) sum = qry(lch[x] , l , mid , L , R);
        if(mid < R) sum += qry(rch[x] , mid + 1 , r , L , R);
        return sum;
    }
}using segt::modify; using segt::qry;
 
namespace LCT{
    int fa[_] , ch[_][2] , val[_] , mn[_]; bool rmrk[_];
 
    bool nroot(int x){return ch[fa[x]][0] == x || ch[fa[x]][1] == x;}
    bool son(int x){return ch[fa[x]][1] == x;}
    void up(int x){mn[x] = min(min(ch[x][0] ? mn[ch[x][0]] : (int)1e9 , ch[x][1] ? mn[ch[x][1]] : (int)1e9) , val[x]);}
    void mark(int x){rmrk[x] ^= 1; swap(ch[x][0] , ch[x][1]);}
    void down(int x){if(rmrk[x]){mark(ch[x][0]); mark(ch[x][1]); rmrk[x] = 0;}}
    void dall(int x){if(nroot(x)) dall(fa[x]); down(x);}
     
    void rot(int x){
        bool f = son(x); int y = fa[x] , z = fa[y] , w = ch[x][f ^ 1];
        fa[x] = z; if(nroot(y)) ch[z][son(y)] = x;
        fa[y] = x; ch[x][f ^ 1] = y;
        ch[y][f] = w; if(w) fa[w] = y;
        up(y);
    }
 
    void splay(int x){
        dall(x);
        while(nroot(x)){
            if(nroot(fa[x])) rot(son(fa[x]) == son(x) ? fa[x] : x);
            rot(x);
        }
        up(x);
    }
 
    void access(int x){for(int y = 0 ; x ; y = x , x = fa[x]){splay(x); ch[x][1] = y; if(y) fa[y] = x; up(x);}}
    void mkrt(int x){access(x); splay(x); mark(x);}
    void split(int x , int y){mkrt(x); access(y); splay(y);}
    void link(int x , int y){mkrt(x); fa[x] = y;}
    void cut(int x , int y){split(x , y); ch[y][0] = fa[x] = 0; up(y);}
    int fdrt(int x){access(x); splay(x); while(down(x) , ch[x][0]) x = ch[x][0]; splay(x); return x;}
}using namespace LCT;
int rt[_] , pre[_] , s[_] , t[_] , N , M , K , TP , lastans;
 
int main(){
    N = read(); M = read(); K = read(); TP = read();
    for(int i = 1 ; i <= M ; ++i){
        s[i] = read() , t[i] = read(); LCT::val[i + N] = LCT::mn[i + N] = i;
    }
     
    for(int i = 1 ; i <= N ; ++i) LCT::val[i] = LCT::mn[i] = 1e9;
    for(int i = 1 ; i <= M ; ++i)
        if(s[i] != t[i]){
            if(fdrt(s[i]) == fdrt(t[i])){
                split(s[i] , t[i]); int id = mn[t[i]];
                pre[i] = id; cut(s[id] , id + N); cut(t[id] , id + N);
            }
            link(s[i] , i + N); link(t[i] , i + N);
        }
        else pre[i] = i;
 
    for(int i = 1 ; i <= M ; ++i) segt::modify(rt[i] = rt[i - 1] , 0 , M , pre[i]);
    for(int i = 1 ; i <= K ; ++i){
        int l = read() ^ (TP * lastans) , r = read() ^ (TP * lastans);
        printf("%d\n" , lastans = N - (segt::qry(rt[r] , 0 , M , 0 , l - 1) - (l - 1)));
    }
    return 0;
}
posted @ 2019-09-12 14:25  cjoier_Itst  阅读(245)  评论(0编辑  收藏  举报