【 bzoj 3514 】Codechef MARCH14 GERALD07加强版 - LCT

  题意简单明了。。。
  考虑从头开始加边。如果还是一棵树的时候,加入了一条边会怎么样。
  如果还是树就不鸟他。
  但是如果构成了环,那么这个环要存在的必要条件显然是最早的边要存在。(当然这个环上的其他边也要存在,但这不重要,因为如果最早的边存在了,这些边肯定会存在),如果将当前边设为 Ej ,这个环上标号最小的边设为 Ei ,那我们称 Ej 依赖于 Ei 。因为如果当前是棵树的情况下,如果 Ei 不在里面,即使 Ej 加入了也对树结构没有影响。因我们可以得到一个算法,按顺序加边,如果加入边 (u,v) 后出现了环,那么就查询 uv 的路径上标号最小的边,并把它删掉,将当前边的权值设为它,然后再加入当前边。区间 [l,r] 内的答案就是n-“区间内权值小于l的数的个数”。
  于是前面的预处理可以拆边用LCT做,后面的查询用可持久化线段树。时间复杂度 O(nlogn) ,在线,常数略大。
  离线的话大概是倒序维护标号最大生成树就可以了吧。
  

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i = a , _ = b ; i <= _ ; i ++)
#define per(i,a,b) for(int i = a , _ = b ; i >= _ ; i --)
#define cr(x) memset(x , 0 , sizeof x)
#define lc ch[u][0]
#define rc ch[u][1]
#define maxn 200007
#define maxm 400007
#define maxs 4000007

inline int rd() {
    char c = getchar();
    while (!isdigit(c)) c = getchar() ; int x = c - '0';
    while (isdigit(c = getchar())) x = x * 10 + c - '0';
    return x;
}

inline void upmin(int&a , int b) { if (a > b) a = b; }

typedef int arr[maxn];
typedef int lct[maxm];
typedef int seg[maxs];

const int inf = 0x7fffffff;

arr x , y , pre , rt;
lct val , rv , mn , fa , sta;

int ch[maxm][2];

int n , m , q , tot , top , Type;

void input() {
    n = rd() , m = rd() , q = rd() , Type = rd();
    rep (i , 1 , m) x[i] = rd() , y[i] = rd();
}

inline bool isrt(int u) {
    return (ch[fa[u]][0] != u) && (ch[fa[u]][1] != u);
}

inline void rev(int u) {
    if (!u) return;
    std::swap(lc , rc);
    rv[u] ^= 1;
}

inline void mt(int u) {
    if (!u) return;
    mn[u] = val[u];
    if (lc) upmin(mn[u] , mn[lc]);
    if (rc) upmin(mn[u] , mn[rc]);
}

inline void ps(int u) {
    if (!u || !rv[u]) return;
    rev(lc) , rev(rc);
    rv[u] = 0;
}

inline void clear(int u) {
    for (sta[top ++] = u;u;u = fa[u]) sta[top ++] = fa[u];
    for (;top;) ps(sta[-- top]);
}

inline void rot(int u) {
    int f = fa[u] , g = fa[f];
    int l = (ch[f][1] == u) , r = l ^ 1;
    if (!isrt(f)) ch[g][ch[g][1] == f] = u;
    fa[u] = g , fa[f] = u; if (ch[u][r]) fa[ch[u][r]] = f;
    ch[f][l] = ch[u][r] , ch[u][r] = f;
    mt(f) , mt(u);
}

inline void splay(int u) {
    for (clear(u);!isrt(u);rot(u)) {
        int f = fa[u] , g = fa[f];
        if (!isrt(f))
            rot(((ch[f][1] == u) ^ (ch[g][1] == f)) ? u : f);
    }
    mt(u);
}

inline void access(int u) {
    int v = u;
    for (int t = 0;u;t = u , u = fa[u])
        splay(u) , rc = t;
    splay(v);
}

inline void mkrt(int u) {
    access(u);
    rev(u);
}

inline void cut(int u , int v) {
    mkrt(v) , access(u);
    fa[lc] = 0 , lc = 0;
    mt(u);
}

inline void link(int u , int v) {
    mkrt(u) , fa[u] = v;
    access(u);
}

inline bool connected(int u , int v) {
    mkrt(v) , access(u);
    while (lc) u = lc;
    if (u != v) return 0;
    return 1;
}

inline int GetMin(int u , int v) {
    mkrt(v) , access(u);
    return mn[u];
}

#undef lc
#undef rc

seg lc , rc , sz;

void update(int pr , int&nr , int l , int r , int p) {
    if (!nr) nr = ++ tot;
    sz[nr] = sz[pr] + 1;
    if (l == r) return;
    int m = l + r >> 1;
    if (p <= m)
        rc[nr] = rc[pr] , update(lc[pr] , lc[nr] , l , m , p);
    else
        lc[nr] = lc[pr] , update(rc[pr] , rc[nr] , m + 1 , r , p);
}

int query(int pr , int nr , int l , int r , int p) {
    if (r <= p) return sz[nr] - sz[pr];
    if (l > p) return 0;
    int m = l + r >> 1;
    int ret = query(lc[pr] , lc[nr] , l , m , p);
    if (p > m) ret += query(rc[pr] , rc[nr] , m + 1 , r , p);
    return ret;
}

void init() {
    rep (i , 1 , n) val[i] = mn[i] = inf;
    rep (i , 1 , m) {
        val[i + n] = mn[i  + n] = i;
        int u = x[i] , v = y[i];
        if (u == v) {
            pre[i] = -1;
            continue;
        }
        if (connected(u , v)) {
            int w = GetMin(u , v);
            cut(x[w] , w + n);
            cut(w + n , y[w]);
            pre[i] = w;
        }
        link(u , i + n);
        link(i + n , v);
    }

    rep (i , 1 , m)
        update(rt[i - 1] , rt[i] , 0 , m , pre[i] == -1 ? m : pre[i]);
}

void solve() {
    init();
    int ans = 0;
    rep (i , 1 , q) {
        int l = rd() , r = rd();
        if (Type)
            l ^= ans , r ^= ans;
        ans = query(rt[l - 1] , rt[r] , 0 , m , l - 1);
        printf("%d\n" , ans = n - ans);
    }
}


int main() {
    #ifndef ONLINE_JUDGE
        freopen("data.txt" , "r" , stdin);
        freopen("data.out" , "w" , stdout);
    #endif
    input();
    solve();
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值