ARC 086 E - Smuggling Marbles(dp + 啓發式合併)

題意

Sunke 有一棵 \(N + 1\) 個點的樹,其中 \(0\) 爲根,每一個點上有 \(0\)\(1\) 個石子, Sunke 會不停的進行以下操做直至整棵樹沒有石子 :c++

  • \(0\) 上面的石子從樹上拿走放入口袋 ;
  • 把每一個點上的石子移到其父親上 ;
  • 對於每一個點 , 若其石子數 \(≥ 2\) , 則移除該點全部石子(不放入口袋)。

求對於全部 \(2^{N+1}​\) 种放置石子的方案 , 最終 Snuke 口袋中石子數是多少 , 對 \(10^9+7​\) 取模 .git

\((1 \le N \le 2000) \ 400\mathrm{pts} \\ (1\le N \le 200000) \ 1000\mathrm{pts}.\)優化

題解

\(400\mathrm{pts}\)spa

咱們不難發現這個操做是層層獨立的... 因此咱們能夠考慮隔離每層來算答案code

考慮一層答案對於最終的貢獻 那麼咱們有一個顯然的 \(dp\)繼承

就是令 \(dp[u][0/1]\)\(u\) 沒/有 石子的方案數 (已經考慮完了 \(u\) 的子樹)get

咱們不難發現 咱們只要考慮它兒子貢獻出來的方案數it

咱們發現有多個石子一塊兒合併上來的方案數很差算... 因此咱們就能夠用全部方案數減去貢獻 \(1\) 個的方案數class

那麼咱們令 \(All\) 爲全部方案數 , 就有im

\[\displaystyle All=\prod_{v \in G[u]} (dp[v][0]+dp[v][1])\]

而後咱們令 \(Zero\) 爲兒子全是 \(0\) 的方案數 , 就有

\[\displaystyle Zero = \prod _{v \in G[u]} dp[v][0]\]

而後又令 \(One\) 爲有一個兒子爲 \(1\) 的方案數 , 就有

\[\displaystyle One = \sum_{v \in G[u]} \frac{Zero \times dp[v][1]}{dp[v][0]}\]

那咱們就能夠輕易更新當前的答案了

\[dp[u][0]=All-One \\ dp[u][1]=One\]

而後每次考慮了一層後 (一開始咱們只初始化了當層的答案)

咱們最後要把 \(dp[0][1]\) 乘上別的層數的方案數 也就是 \(2^{n + 1- tot[dep]}\) 而後加起來就是答案了..(代碼見文末)

那麼 \(400\mathrm{pts}\) 就到手了qwq


而後咱們考慮一下如何優化

有一個常常使用的套路 那麼就是啓發式合併了...

咱們把兒子 \(dp\) 狀態最多繼承上來 而後其餘的狀態暴力合併上去

把別的 \(dp\) 狀態暴力合併上來就好了

爲了方便轉移 和 空間問題 咱們每一個點要動態開空間

就是咱們每一個點開個 vector<pair<long long, long long> >

它的下標從大到小 表示 當前點向下的深度從小到大

first 表明原來的 [0] ; second 表明原來的 [1] .

而後轉移的時候下標就有些細節要注意一下

而後分析一波時間複雜度qwq

實際上是 \(O(n)\) 的 , 由於兩個狀態只會在其 \(\mathrm{LCA}\) 上合併,而後同一層兩兩點的 \(\mathrm{LCA}\) 只會有該層點數 \(−1\) 個。

但我須要求一個逆元 時間複雜度就變成 $O(n \log n) $ 了.... 但仍是速度還行 (267ms)

那個若是用前綴積 和 後綴積 的話就能夠優化成 \(O(n)\)可是不想寫了...

代碼

\(400\mathrm{pts}\)

#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
 
inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
 
inline int read() {
    int x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * fh;
}
 
void File() {
#ifdef zjp_shadow
    freopen ("E.in", "r", stdin);
    freopen ("E.out", "w", stdout);
#endif
}
 
typedef long long ll;
const ll Mod = 1e9 + 7;
const int N = 2010;
ll dp[N][2], ans = 0;
ll fpm(ll x, ll power) {
    ll res = 1;
    for (; power; power >>= 1, (x *= x) %= Mod)
        if (power & 1) (res *= x) %= Mod;
    return res;
}
 
int fa[N], n, dep[N], tot[N];
vector<int> G[N];
 
int main () {
    File();
    n = read();
    For (i, 1, n) {
        fa[i] = read();
        dep[i] = dep[fa[i]] + 1;
        G[fa[i]].push_back(i);
        ++ tot[dep[i]];
    }
    ++ tot[0];
    For (d, 0, n) {
        Fordown(i, n, 0) {
            if (dep[i] > d) continue ;
            if (dep[i] == d) {
                dp[i][0] = dp[i][1] = 1;
                continue ;
            }
            ll All = 1, Zero = 1;
            for (int v : G[i]) {
                (All *= (dp[v][0] + dp[v][1]) % Mod) %= Mod;
                (Zero *= dp[v][0]) %= Mod;
            }
            ll One = 0;
            for (int v : G[i])
                (One += Zero * fpm(dp[v][0], Mod - 2) % Mod * dp[v][1] % Mod) %= Mod;
            dp[i][0] = ((All - One) % Mod + Mod) % Mod;
            dp[i][1] = One;
        }
        (ans += dp[0][1] * fpm(2, n + 1 - tot[d]) % Mod) %= Mod;
    }
    printf ("%lld\n", ans);
    return 0;
}

\(1000\mathrm{pts}\)

#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
 
inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
 
inline int read() {
    int x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * fh;
}
 
void File() {
#ifdef zjp_shadow
    freopen ("E.in", "r", stdin);
    freopen ("E.out", "w", stdout);
#endif
}
 
typedef long long ll;
typedef pair<ll, ll> pll;
#define fir first
#define sec second
#define mp make_pair
const ll Mod = 1e9 + 7;
const int N = 201000;
 
ll ans = 0;
ll fpm(ll x, ll power) {
    ll res = 1;
    for (; power; power >>= 1, (x *= x) %= Mod)
        if (power & 1) (res *= x) %= Mod;
    return res;
}
 
int fa[N], n, tot[N];
vector<int> G[N];
 
int id[N], num = 0, d[N];
vector<pll> dp[N];
 
ll All[N], Zero[N], One[N];
 
void Dfs(int u) {
    int son = n + 1;
    for (int v : G[u]) { Dfs(v); if (d[v] > d[son]) son = v;}
    if (son != n + 1) id[u] = id[son], d[u] = d[son] + 1;
    else id[u] = ++num;
    dp[id[u]].push_back(mp(1, 1));
 
    if ((int)G[u].size() == 1) return ;
 
    For (i, 0, d[u] - 1)
        All[i] = 1, Zero[i] = 1, One[i] = 0;
 
    int nowdep;
    for (int v : G[u])
        For (i, 0, d[v]) {
            nowdep = (d[son] - d[v]) + i;
            pll sta = dp[id[v]][i];
            (All[nowdep] *= (sta.fir + sta.sec)) %= Mod;
            (Zero[nowdep] *= sta.fir) %= Mod;
        }
 
    for (int v : G[u])
        For (i, 0, d[v]) {
            nowdep = (d[son] - d[v]) + i;
            pll sta = dp[id[v]][i];
            (One[nowdep] += Zero[nowdep] * fpm(sta.fir, Mod - 2) % Mod * sta.sec % Mod) %= Mod;
        }
 
    For (i, 0, d[u] - 1) {
        dp[id[u]][i].fir = (All[i] - One[i] + Mod) % Mod;
        dp[id[u]][i].sec = One[i];
    }
}
 
int dep[N];
 
int main () {
    File();
    n = read();
    For (i, 1, n) {
        fa[i] = read();
        G[fa[i]].push_back(i);
        dep[i] = dep[fa[i]] + 1;
        ++ tot[dep[i]];
    }
    ++ tot[0];
    d[n + 1] = -1;
 
    Dfs(0);
 
    For (i, 0, d[0]) {
        pll sta = dp[id[0]][i];
        (ans += sta.sec * fpm(2, n + 1 - tot[d[0] - i]) % Mod) %= Mod;
    }
    printf ("%lld\n", ans);
    return 0;
}
相關文章
相關標籤/搜索