Sunke 有一棵 \(N + 1\) 个点的树,其中 \(0\) 为根,每一个点上有 \(0\) 或 \(1\) 个石子, Sunke 会不停的进行以下操做直至整棵树没有石子 :c++
求对于全部 \(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; }