[链分治][FWT][树链剖分] BZOJ 4911: [Sdoi2017]切树游戏

8 篇文章 0 订阅
2 篇文章 0 订阅

Solution

DP是一个异或卷积的形式,可以FWT把 O(128) 的复杂度降下来。
链分治参考immortalCO的博客
最后还是有些地方 0 <script type="math/tex" id="MathJax-Element-88">0</script>的逆元的问题搞错了。。

#include <bits/stdc++.h>
using namespace std;

const int N = 60303;
const int M = 130;
const int MOD = 10007;
const int INV2 = (MOD + 1) >> 1;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}
inline void reado(char &c) {
    for (c = get(); c != 'Q' && c != 'C'; c = get());
}

struct edge {
    int to, next;
    edge(int t = 0, int n = 0):to(t), next(n) {}
};
edge G[N << 1];
int head[N];
int Gcnt, Tcnt, n, m, q, x, y, lim, tnt;
int top[N], size[N], son[N], fa[N], dep[N];
int inv[N], pos[N], tps[N], w[N];
vector<int> s[N];
struct Int {
    int d, c;
    Int(void) {}
    Int(int x) {
        x %= MOD;
        if (x) d = x, c = 0;
        else d = 1, c = 1;
    }
    inline Int &operator *=(int x) {
        x %= MOD; if (x == 0) ++c;
        else d = d * x % MOD;
        return *this;
    }
    inline Int &operator /=(int x) {
        x %= MOD; if (x == 0) --c;
        else d = d * inv[x] % MOD;
        return *this;
    }
    inline int val(void) {
        return c ? 0 : d;
    }
};
int num[M][M];
Int lt[N][M];
int rt[N], ans[N], res[N];
int sum[N * 3][M], lval[N * 3][M], rval[N * 3][M], val[N * 3][M];
int ls[N * 3], rs[N * 3], par[N * 3];
char opt;

inline void Add(int &x, int a) {
    x = (x + a) % MOD;
}
inline void AddEdge(int from, int to) {
    G[++Gcnt] = edge(from, head[to]); head[to] = Gcnt;
    G[++Gcnt] = edge(to, head[from]); head[from] = Gcnt;
}
inline void dfs1(int u) {
    int to; size[u] = 1;
    for (int i = head[u]; i; i = G[i].next) {
        to = G[i].to; if (to == fa[u]) continue;
        fa[to] = u; dep[to] = dep[u] + 1;
        dfs1(to); size[u] += size[to];
        if (size[to] > size[son[u]]) son[u] = to;
    }
}
inline void dfs2(int u, int t) {
    top[u] = t; s[t].push_back(u);
    if (son[u]) dfs2(son[u], t);
    for (int i = head[u]; i; i = G[i].next)
        if (G[i].to != fa[u] && G[i].to != son[u])
            dfs2(G[i].to, G[i].to);
}
inline void FWT(int* a, int n, int f) {
    static int x, y;
    for (int i = 1; i < n; i <<= 1)
        for (int j = 0; j < n; j += (i << 1))
            for (int k = 0; k < i; k++) {
                x = a[j + k]; y = a[j + k + i];
                a[j + k] = (x + y) % MOD;
                a[j + k + i] = (x - y + MOD) % MOD;
                if (f == -1) {
                    a[j + k] = a[j + k] * INV2 % MOD;
                    a[j + k + i] = a[j + k + i] * INV2 % MOD;
                }
            }
}
inline void Prep(int m) {
    inv[1] = 1;
    for (int i = 2; i < MOD; i++)
        inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
    for (int i = 0; i < m; i++) {
        num[i][i] = 1; FWT(num[i], m, 1);
    }
}
inline void PushUp(int o) {
    int L = ls[o], R = rs[o];
    for (int i = 0; i < lim; i++) {
        val[o][i] = (val[L][i] + val[R][i] + rval[L][i] * lval[R][i]) % MOD;
        lval[o][i] = (lval[L][i] + lval[R][i] * sum[L][i]) % MOD;
        rval[o][i] = (rval[R][i] + rval[L][i] * sum[R][i]) % MOD;
        sum[o][i] = sum[L][i] * sum[R][i] % MOD;
    }
}
inline void Build(int &o, int l, int r, int t) {
    o = ++Tcnt;
    if (l == r) {
        for (int i = 0; i < lim; i++)
            sum[o][i] = lval[o][i] = rval[o][i] = 
            val[o][i] = lt[s[t][l - 1]][i].val();
        pos[s[t][l - 1]] = o; return;
    }
    int mid = (l + r) >> 1;
    Build(ls[o], l, mid, t);
    Build(rs[o], mid + 1, r, t);
    PushUp(o); par[ls[o]] = par[rs[o]] = o;
}
inline void Modify(int u) {
    int t = top[u];
    if (fa[t])
        for (int i = 0; i < lim; i++)
            lt[fa[t]][i] /= (lval[rt[t]][i] + num[0][i]) % MOD;
    for (int i = 0; i < lim; i++)
        Add(ans[i], MOD - val[rt[t]][i]);
    int o = pos[u];
    for (int i = 0; i < lim; i++)
        sum[o][i] = lval[o][i] = rval[o][i] = 
        val[o][i] = lt[u][i].val();
    o = par[o];
    while (o) {
        PushUp(o); o = par[o];
    }
    if (fa[t])
        for (int i = 0; i < lim; i++)
            lt[fa[t]][i] *= (lval[rt[t]][i] + num[0][i]) % MOD;
    for (int i = 0; i < lim; i++)
        Add(ans[i], val[rt[t]][i]);
}
inline bool cmp(int x, int y) {
    return dep[x] > dep[y];
}

int main(void) {
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    read(n); read(m);
    for (lim = 1; lim < m; lim <<= 1);
    Prep(lim);
    for (int i = 1; i <= n; i++) {
        read(w[i]);
        for (int j = 0; j < lim; j++)
            lt[i][j] = Int(num[w[i]][j]);
    }
    for (int i = 1; i < n; i++) {
        read(x); read(y);
        AddEdge(x, y);
    }
    dfs1(1); dfs2(1, 1);
    for (int i = 1; i <= n; i++)
        if (top[i] == i) tps[++tnt] = i;
    sort(tps + 1, tps + tnt + 1, cmp);
    for (int i = 1; i <= tnt; i++) {
        int x = tps[i];
        Build(rt[x], 1, s[x].size(), x);
        if (fa[x])
            for (int j = 0; j < lim; j++)
                lt[fa[x]][j] *= (lval[rt[x]][j] + num[0][j]) % MOD;
        for (int j = 0; j < lim; j++)
            Add(ans[j], val[rt[x]][j]);
    }
    read(q);
    while (q--) {
        reado(opt); 
        if (opt == 'C') {
            read(x); read(y);
            for (int i = 0; i < lim; i++)
                lt[x][i] /= num[w[x]][i];
            w[x] = y;
            for (int i = 0; i < lim; i++)
                lt[x][i] *= num[w[x]][i];
            while (x) {
                Modify(x); x = fa[top[x]];
            }
        } else {
            read(x);
            for (int i = 0; i < lim; i++) res[i] = ans[i];
            FWT(res, lim, -1);
            printf("%d\n", res[x]);
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值