【ARC086E】Smuggling Marbles(虚树,树形DP)

15 篇文章 0 订阅
3 篇文章 0 订阅

Description

给出一棵 N 个点的有根树 , 初始时其中一些点上有一个石子 , 每次同时将所有石子从所在的点移动到父亲上 , 根节点上的石子移动到篮子里 .如果有一个点上的石子数大于 1 则移除所有石子 , 树上没有石子时结束 .求所有 2 N 种初始局面经过操作后篮子里石子的总数量 .


Solution

我们发现每一层的点都是独立的,考虑分开处理,分别将每一层的点建成虚树后树形DP即可,时间复杂度 O(nlogn) O ( n log ⁡ n )
可以用长链剖分做到 O(n) O ( n )


Code

/************************************************
 * Au: Hany01
 * Date: Aug 25th, 2018
 * Prob: ARC086 E
 * Email: hany01dxx@gmail.com & hany01@foxmail.com
 * Inst: Yali High School
************************************************/

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef long double LD;
typedef pair<int, int> PII;
#define rep(i, j) for (register int i = 0, i##_end_ = (j); i < i##_end_; ++ i)
#define For(i, j, k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define x first
#define y second
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define SZ(a) ((int)(a).size())
#define ALL(a) a.begin(), a.end()
#define INF (0x3f3f3f3f)
#define INF1 (2139062143)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define y1 wozenmezhemecaia

template <typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }

inline int read() {
    static int _, __; static char c_;
    for (_ = 0, __ = 1, c_ = getchar(); c_ < '0' || c_ > '9'; c_ = getchar()) if (c_ == '-') __ = -1;
    for ( ; c_ >= '0' && c_ <= '9'; c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ ^ 48);
    return _ * __;
}

const int maxn = 2e5 + 5, MOD = 1e9 + 7;

int n, fa[19][maxn], v[maxn], nex[maxn], beg[maxn], e, v_[maxn], nex_[maxn], beg_[maxn], e_, dfn[maxn], clk, dep[maxn], dp[maxn][2], stk[maxn], top, Ans, sz[maxn];
vector<int> vec[maxn];

inline void add(int uu, int vv) { v[++ e] = vv, nex[e] = beg[uu], beg[uu] = e; }
inline void add_(int uu, int vv) { v_[++ e_] = vv, nex_[e_] = beg_[uu], beg_[uu] = e_; }
inline int  ad(int x, int y) { if ((x += y) >= MOD) return x - MOD; return x; }
inline int  Pow(int a, int b) {
    static int Ans;
    for (Ans = 1; b; b >>= 1, a = (LL)a * a % MOD) if (b & 1) Ans = (LL)Ans * a % MOD;
    return Ans;
}

void DFS(int u) {
    vec[dep[u]].pb(u), dfn[u] = ++ clk;
    For(i, 1, 17) fa[i][u] = fa[i - 1][fa[i - 1][u]];
    for (register int i = beg[u]; i; i = nex[i])
        dep[v[i]] = dep[u] + 1, DFS(v[i]);
}

inline int LCA(int u, int v) {
    if (dep[u] < dep[v]) swap(u, v);
    Fordown(i, 17, 0) if (dep[fa[i][u]] >= dep[v]) u = fa[i][u];
    if (u == v) return u;
    Fordown(i, 17, 0) if (fa[i][u] ^ fa[i][v]) u = fa[i][u], v = fa[i][v];
    return fa[0][u];
}

inline bool cmp(int x, int y) { return dfn[x] < dfn[y]; }

void DP(int u) {
    if (!beg_[u]) { dp[u][0] = dp[u][1] = sz[u] = 1; return; }
    sz[u] = 0;
    int prod = 1;
    for (register int i = beg_[u]; i; i = nex_[i])
        DP(v_[i]), prod = (LL)prod * dp[v_[i]][0] % MOD, sz[u] += sz[v_[i]];
    dp[u][1] = 0;
    for (register int i = beg_[u]; i; i = nex_[i])
        dp[u][1] = ad(dp[u][1], (LL)dp[v_[i]][1] * Pow(dp[v_[i]][0], MOD - 2) % MOD * prod % MOD);
    dp[u][0] = ad(Pow(2, sz[u]), MOD - dp[u][1]), beg_[u] = 0;
}

int main()
{
#ifdef hany01
    freopen("arc086e.in", "r", stdin);
    freopen("arc086e.out", "w", stdout);
#endif

    n = read() + 1;
    For(i, 2, n) fa[0][i] = read() + 1, add(fa[0][i], i);
    dep[1] = 1, DFS(1);

    for (register int dpt = 1, sz; ; ++ dpt) {
        if (!(sz = SZ(vec[dpt]))) break;
        sort(ALL(vec[dpt]), cmp), stk[top = 1] = 1, e_ = 0;
        For(i, vec[dpt][0] == 1, sz - 1) {
            register int lca = LCA(vec[dpt][i], stk[top]);
            if (lca != stk[top]) {
                while (top > 1 && dep[stk[top - 1]] >= dep[lca]) add_(stk[top - 1], stk[top]), -- top;
                if (stk[top] != lca) add_(lca, stk[top]), stk[top] = lca;
            }
            stk[++ top] = vec[dpt][i];
        }
        Fordown(i, top, 2) add_(stk[i - 1], stk[i]);
        DP(1), Ans = ad(Ans, (LL)dp[1][1] * Pow(2, n - sz) % MOD);
    }
    printf("%d\n", Ans);

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值