神奇的樹上啓發式合併 (dsu on tree)

參考資料

http://www.javashuo.com/article/p-tldaqyfl-bo.html
https://www.cnblogs.com/candy99/p/dsuontree.html
http://www.javashuo.com/article/p-rqtzrwgk-n.htmlhtml

簡介

樹上啓發式合併
用到了heavy−light decomposition樹鏈剖分
把輕邊子樹的信息合併到重鏈上的點裏
由於每次都是先dfs輕兒子再dfs重兒子,只有重兒子子樹的貢獻保留,因此能夠保證dfs到每顆子樹時當前全局維護的信息不會有別的子樹裏的,和莫隊很像node

算法實現

1.遍歷輕兒子
2.遍歷重兒子(保留數據)
3.計算全部輕兒子爲根的子樹
4.若是當前點是輕兒子,清空影響c++

複雜度分析

樹鏈剖分後每一個點到根的路徑上有\(logn\)條輕邊和\(logn\)條重鏈算法

每一個點碰見輕邊時合併一次,因此至多\(logn\)spa

總複雜度\(O(nlogn)\)code

例題

CF600E. Lomsat gelral

http://codeforces.com/contest/600/problem/E
題意:詢問每顆子樹中出現次數最多的顏色們編號和htm

板子題blog

Codeget

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 1e5+10;
int c[N], n;
struct node {
    int to, nxt;
}g[N<<1];
int last[N], gl;

void add(int x, int y) {
    g[++gl] = (node) {y, last[x]};
    last[x] = gl;
}
int son[N], siz[N];
void dfs1(int u, int f) {
    siz[u] = 1;
    for (int i = last[u]; i; i = g[i].nxt) {
        int v = g[i].to;
        if (v == f) continue;
        dfs1(v, u);
        siz[u] += siz[v];       
        if (siz[son[u]] < siz[v]) son[u] = v;
    }
    return ;
}

int num[N], top;
LL sum[N], ans[N];
bool vis[N];

void calc(int u, int fa, int k) {
    sum[num[c[u]]] -= c[u];
    num[c[u]] += k;
    sum[num[c[u]]] += c[u];
    if (sum[top + 1]) top++;
    else if (!sum[top]) top--;

    for (int i = last[u]; i; i = g[i].nxt) {
        int v = g[i].to;
        if (v == fa || vis[v]) continue;
        calc(v, u, k);
    }
    return ;
}

void dfs(int u, int fa, int op) {
    for (int i = last[u]; i; i = g[i].nxt)
        if (g[i].to != fa && g[i].to != son[u])
            dfs(g[i].to, u, 0);
    if (son[u])
        dfs(son[u], u, 1), vis[son[u]] = 1;
    calc(u, fa, 1); vis[son[u]] = 0;
    ans[u] = sum[top];
    if (!op) calc(u, fa, -1);
    return ;
}


int main() {
    read(n);
    for (int i = 1; i <= n; i++) read(c[i]);
    for (int i = 1; i < n; i++) {
        int x, y; read(x), read(y);
        add(x, y), add(y, x);
    }
    dfs1(1, 0);
    dfs(1, 0, 1);
    for (int i = 1; i <= n; i++)
        printf("%I64d ", ans[i]);
    return 0;
}

CF570D Tree Requests

http://codeforces.com/problemset/problem/570/D
https://www.luogu.org/problemnew/show/CF570Drequests

構成迴文串,奇數個的字母至多一個
用二進制狀壓判斷便可

\(sum[x]\)表示深度爲\(x\)構成的狀態

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}

const int N = 500010;

struct node {
    int to, nxt;
}g[N<<1], q[N];
int last[N], gl;
int n, m;
void add(int x, int y) {
    g[++gl] = (node) {y, last[x]};
    last[x] = gl;
    g[++gl] = (node) {x, last[y]};
    last[y] = gl;
}

char s[N];
int siz[N], son[N], val[N], dep[N], sum[N];
bool vis[N];
void dfs1(int u, int fa) {
    siz[u] = 1;
    for (int i = last[u]; i; i = g[i].nxt) {
        int v = g[i].to;
        if (v == fa) continue;
        dep[v] = dep[u] + 1;
        dfs1(v, u);     
        siz[u] += siz[v];
        if (siz[son[u]] < siz[v]) son[u] = v;
    }   
}

void calc(int u, int fa) {
    sum[dep[u]] ^= val[u];
    for (int i = last[u]; i; i = g[i].nxt) {
        int v = g[i].to;
        if (v == fa || vis[v]) continue;
        calc(v, u);
    }
    return ;
}

struct Node {
    int h, nxt;
}a[N];
bool ans[N];
int S[N];

bool cal(int x) {
    int tmp = 0;
    while (x) {
        tmp++;
        x -= (x & (-x));
    }
    return tmp <= 1;
}

void dfs(int u, int fa, int op) {
    for (int i = last[u]; i; i = g[i].nxt) {
        int v = g[i].to;
        if (v == fa || son[u] == v) continue;
        dfs(v, u, 0);
    }
    if (son[u]) dfs(son[u], u, 1), vis[son[u]] = 1;
    calc(u, fa); vis[son[u]] = 0;
    for (int i = S[u]; i; i = a[i].nxt)
        ans[i] = cal(sum[a[i].h]);
    if (!op) calc(u, fa);
    return ;
}


int main() {
    read(n), read(m);
    for (int i = 2; i <= n; i++) {
        int x; read(x);
        add(x, i);
    }
    scanf("%s", s+1);
    for (int i = 1; i <= n; i++) val[i] = 1<<(s[i]-'a');
    dep[1] = 1;
    dfs1(1, 0);
    for (int i = 1; i <= m; i++) {
        int h, v;
        read(v), read(h);
        a[i].nxt = S[v];
        S[v] = i; a[i].h = h;       
    }
    dfs(1, 0, 1);
    for (int i = 1; i <= m; i++) puts(ans[i] ? "Yes" : "No");
    return 0;
}
相關文章
相關標籤/搜索