1. 程式人生 > >luogu3703 [SDOI2017]樹點塗色(線段樹+樹鏈剖分+動態樹)

luogu3703 [SDOI2017]樹點塗色(線段樹+樹鏈剖分+動態樹)

你會 bool vector root 動態樹 說我 scan 很難 第一個

link

你谷的第一篇題解沒用寫LCT,然後沒觀察懂,但是自己YY了一種不用LCT的做法

我們考慮對於每個點,維護一個fa,代表以1為根時候這個點的父親
再維護一個bel,由於一個顏色相同的段一定是一個深度遞增的鏈,這個代表顏色段的鏈頂
再維護一個ans,就是ans
那麽第二個操作就是ans單點查詢,第三個就是ans區間最大值
第一個操作,我們樹剖把它變成了log條重鏈,在重鏈上的點的bel和ans可以區間修改
但是重鏈上每個點的兒子就說gg了
這些兒子的子樹減去的是他們的各自bel的fa的點權,然後在加上一個1
所以說每個點還要維護一個preferred child,顯然很難寫(但是應該是可以實現的),還是寫動態樹吧

動態樹其實是動態樹的閹割版,我們用一個splay維護樹中顏色相同的點,顯然這種點一定是一段深度遞增的鏈,於是你會發現只需要欽定1為根,操作1就是access

然後我們可以維護每個點操作3的ans,再搞個dfn,操作3就可以用線段樹維護,然而每個點的答案就相當於動態樹上這個點到根節點上虛邊的個數+1,我們可以搞個線段樹維護,在動態樹access虛實變化時候搞個區間加法即可

然後你會發現對於操作2的x,y答案就是ans[x] + ans[y] - 2 * ans[lc] + 1,所以說我們還需要支持一個lca操作,寫個樹剖或者倍增就行了,我為了湊齊全家桶就寫了個樹剖

註意樹剖的fa和lct的fa一開始是一個fa,lct的fa是動態維護的,樹剖的fa是靜態的,要開兩個數組並且別弄混了,調了好長時間...

access過程中由於變換的是深度恰好為depth[x]+1的點,所以就要對x尋找後繼,就是x的右兒子不停往左跳。。。

時間復雜度Nlog^2N

#include <cstdio>
#include <vector>
using namespace std;

//區間加法,區間取max
int fa[100010], dfn[100010], depth[100010], weight[100010], wson[100010], top[100010];
int tree[400010], lazy[400010], fuck[100010], ch[100010][2], fat[100010], tot, n, m;
vector<int> out[100010];

void init(int x, int cl, int cr)
{
    if (cl == cr)
    {
        tree[x] = fuck[cl];
    }
    else
    {
        int mid = (cl + cr) / 2;
        init(x * 2, cl, mid), init(x * 2 + 1, mid + 1, cr);
        tree[x] = max(tree[x * 2], tree[x * 2 + 1]);
    }
}

void pushdown(int x)
{
    tree[x * 2] += lazy[x], tree[x * 2 + 1] += lazy[x];
    lazy[x * 2] += lazy[x], lazy[x * 2 + 1] += lazy[x];
    lazy[x] = 0;
}

void chenge(int x, int cl, int cr, int L, int R, int val)
{
    if (cr < L || R < cl) return;
    if (L <= cl && cr <= R) { tree[x] += val, lazy[x] += val; return; }
    pushdown(x);
    int mid = (cl + cr) / 2;
    chenge(x * 2, cl, mid, L, R, val);
    chenge(x * 2 + 1, mid + 1, cr, L, R, val);
    tree[x] = max(tree[x * 2], tree[x * 2 + 1]);
}

int query(int x, int cl, int cr, int L, int R)
{
    if (cr < L || R < cl) return 0;
    if (L <= cl && cr <= R) return tree[x];
    pushdown(x);
    int mid = (cl + cr) / 2;
    return max(query(x * 2, cl, mid, L, R), query(x * 2 + 1, mid + 1, cr, L, R));
}

void dfs1(int x)
{
    weight[x] = 1, wson[x] = -1;
    for (int i : out[x]) if (fa[x] != i)
    {
        fa[i] = fat[i] = x, depth[i] = depth[x] + 1;
        dfs1(i), weight[x] += weight[i];
        if (wson[x] == -1 || weight[wson[x]] < weight[i]) wson[x] = i;
    }
}

void dfs2(int x, int topf)
{
    dfn[x] = ++tot, top[x] = topf, fuck[dfn[x]] = depth[x] + 1;
    if (wson[x] == -1) return;
    dfs2(wson[x], topf);
    for (int i : out[x]) if (fa[x] != i && wson[x] != i) dfs2(i, i);
}

int lca(int x, int y)
{
    while (top[x] != top[y])
    {
        if (depth[top[x]] < depth[top[y]]) swap(x, y);
        x = fa[top[x]];
    }
    if (depth[x] > depth[y]) swap(x, y);
    return x;
}

bool nroot(int x) { return ch[fat[x]][0] == x || ch[fat[x]][1] == x; }

void rotate(int x)
{
    int y = fat[x], z = fat[y], k = ch[y][1] == x, w = ch[x][k ^ 1];
    if (nroot(y)) { ch[z][ch[z][1] == y] = x; } ch[x][k ^ 1] = y, ch[y][k] = w;
    if (w) { fat[w] = y; } fat[y] = x; fat[x] = z;
}

void splay(int x)
{
    while (nroot(x))
    {
        int y = fat[x], z = fat[y];
        if (nroot(y)) rotate((ch[y][1] == x) ^ (ch[z][1] == y) ? x : y);
        rotate(x);
    }
}

int findrt(int x)
{
    while (ch[x][0]) x = ch[x][0];
    return x;
}

void access(int x)
{
    for (int y = 0; x > 0; x = fat[y = x])
    {
        splay(x);
        if (ch[x][1])
        {
            int p = findrt(ch[x][1]);
            chenge(1, 1, n, dfn[p], dfn[p] + weight[p] - 1, 1);
        }
        ch[x][1] = y;
        if (y)
        {
            int p = findrt(y);
            chenge(1, 1, n, dfn[p], dfn[p] + weight[p] - 1, -1);
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int x, y, i = 1; i < n; i++) scanf("%d%d", &x, &y), out[x].push_back(y), out[y].push_back(x);
    dfs1(1), dfs2(1, 1); init(1, 1, n);
    for (int opd, x, y, i = 1; i <= m; i++)
    {
        scanf("%d%d", &opd, &x);
        if (opd == 1) access(x);
        if (opd == 2)
        {
            scanf("%d", &y);
            int lc = lca(x, y);
            printf("%d\n", query(1, 1, n, dfn[x], dfn[x]) + query(1, 1, n, dfn[y], dfn[y]) - 2 * query(1, 1, n, dfn[lc], dfn[lc]) + 1);
        }
        if (opd == 3) printf("%d\n", query(1, 1, n, dfn[x], dfn[x] + weight[x] - 1));
    }
    return 0;
}

luogu3703 [SDOI2017]樹點塗色(線段樹+樹鏈剖分+動態樹)