BZOJ3514

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"><span style="white-space:pre">	</span>好久沒有寫動態樹了,都快忘了,今天來複習一下</span>

題意:給定N個點M條邊的圖,詢問選第L到R條邊時該圖的聯通塊個數。

我就直接講做法了:以每條邊的編號爲每條邊的邊權,然後用LCT維護最大生成樹,對於當前的一條邊x->y,如果x和y不聯通,那麼把x和y加入生成樹,如果x和y聯通,那麼在x到y的路徑上選取一條權值最小的邊,砍了,然後連接x和y,此時記錄每條邊彈出的邊的編號(記爲num),如果不連通就爲0,如果是自環就忽略。然後在詢問的時候,查詢L到R之間小於L的num的個數,這個用樹套樹或者用可持久化線段樹維護一下就好了,然後n - ans就是每組詢問的答案,至於爲什麼是這麼做,請讀者自行思考,這應該是很簡單的。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXX = 400005;
int x, y, i, j, k, n, m, qq;
int lc[MAXX], rc[MAXX], fa[MAXX], sum[MAXX], sum1[MAXX], cnt[MAXX], key[MAXX], num[MAXX], next[2][MAXX];
int ls[MAXX << 4], rs[MAXX << 4], sum2[MAXX << 4], len = 0, ans, root[MAXX], ty;
int q[MAXX];
inline int get()
{
    char c;
    while ((c = getchar()) < 48 || c > 57);
    int res = c - 48;
    while ((c = getchar()) >= 48 && c <= 57)
    res = res * 10 + c - 48;
    return res;
}
inline int isroot(int x)
{
    return ((lc[fa[x]] == x) || (rc[fa[x]] == x));
}
inline void rev(int x)
{
    cnt[x] ^= 1;
    swap(lc[x], rc[x]);
}
inline void putdown(int x)
{
    if (cnt[x])
    {
        cnt[x] = 0;
        if (lc[x]) rev(lc[x]);
        if (rc[x]) rev(rc[x]);
    }
}
inline void pushup(int x)
{
    sum1[x] = x;
    sum[x] = key[x];
    if (lc[x] && sum[lc[x]] < sum[x]) sum[x] = sum[lc[x]], sum1[x] = sum1[lc[x]];
    if (rc[x] && sum[rc[x]] < sum[x]) sum[x] = sum[rc[x]], sum1[x] = sum1[rc[x]];
}
inline void turn(int x)
{
    int y = fa[x], z = fa[y], b = 0;
    if (x == lc[y]) b = rc[x];
    else b = lc[x];
    if (b) fa[b] = y;
    fa[x] = z; fa[y] = x;
    if (z)
        if (lc[z] == y) lc[z] = x;
        else if (rc[z] == y) rc[z] = x;
    if (lc[y] == x) rc[x] = y, lc[y] = b;
    else lc[x] = y, rc[y] = b;
    pushup(y);
}
inline void splay(int x)
{
    int len = 0, i;
    for(i = x; isroot(i); i = fa[i])
    q[++len] = i;
    q[++len] = i;
    while (len) putdown(q[len--]);
    while (isroot(x))
    {
        if (isroot(fa[x]))
            if ((lc[fa[x]] == x) == (lc[fa[fa[x]]] == fa[x])) turn(fa[x]);
            else turn(x);
        turn(x);
    }
    pushup(x);
}
inline void lct_access(int x)
{
    int sb = 0;
    while (x)
    {
        splay(x);
        rc[x] = sb;
        sb = x;
        x = fa[x];
    }
}
inline void makeroot(int x)
{
    lct_access(x);
    splay(x);
    rev(x);
}
inline void LINK(int x, int y)
{
    makeroot(x); fa[x] = y;
}
inline void lct_cut(int x, int y)
{
    makeroot(x);
    lct_access(y); splay(y);
    fa[x] = lc[y] = 0;
    pushup(y);
}
inline int check(int x, int y)
{
    int xx = x, yy = y;
    lct_access(xx); splay(xx);
    while (lc[xx]) xx = lc[xx];
    lct_access(yy); splay(yy);
    while (lc[yy]) yy = lc[yy];
    return (xx == yy);
}
inline void split(int x, int y)
{
    makeroot(y);
    lct_access(x);
    splay(x);
}
inline void insert(int &k, const int &y, const int &p, const int &q, const int &w)
{
    k = ++len;
    sum2[k] = sum2[y];
    ls[k] = ls[y];
    rs[k] = rs[y];
    sum2[k]++;
    if (p == q) return;
    int mid = (p + q) >> 1;
    if (mid >= w) insert(ls[k], ls[y], p, mid, w);
    else insert(rs[k], rs[y], mid + 1, q, w);
}
inline void find(const int &x, const int &y, const int &p, const int &q, const int &l, const int &r)
{
    if (p >= l && q <= r)
    {
        ans += sum2[x] - sum2[y];
        return;
    }
    int mid = (p + q) >> 1;
    if (mid >= l) find(ls[x], ls[y], p, mid, l, r);
    if (mid < r) find(rs[x], rs[y], mid + 1, q, l, r);
}
int main()
{
    cin >> n >> m >> qq >> ty;
    for(i = 1; i <= n; i ++)
    key[i] = m + 1;
    for(i = 1; i <= m; i ++)
    {
        x = get(); y = get();
        key[i + n] = i;
        next[0][i + n] = x;
        next[1][i + n] = y;
        if (x == y) {num[i] = m + 1; continue;}
        if (!check(x, y)) LINK(x, i + n), LINK(i + n, y);
        else{
            split(x, y);
            num[i] = sum[x];
            k = sum1[x];
            lct_cut(next[0][k], k);
            lct_cut(next[1][k], k);
            LINK(x, i + n);
            LINK(i + n, y);
        }
    }
    for(i = 1; i <= m; i ++)
    insert(root[i], root[i - 1], 0, m + 1, num[i]);
    m++;
    for(i = 1; i <= qq; i ++)
    {
        x = get(); y = get();
        if (ty) x ^= ans, y ^= ans;
        if (x > y) swap(x, y);
        ans = 0;
        find(root[y], root[x - 1], 0, m, 0, x - 1);
        printf("%d\n", ans = n - ans);
    }
    return 0;
}




發佈了44 篇原創文章 · 獲贊 11 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章