[DP记录]AT3721 [ARC086C] Smuggling Marbles

command_block

2021-05-12 07:38:03

Personal

**题意** : 给出一棵 $n+1$ 个点的树,编号 $0\sim n$ ,其中0为根。 每个点上有 $0$ 或 $1$ 个石子,重复进行如下操作直至整棵树没有石子: - 把 $0$ 上面的石子从树上拿走放入口袋。 - 把每个点上的石子移到其父亲上。 - 对于每个点,若其石子数 $≥2$,则移除该点所有石子(不放入口袋)。 求对于所有$2^{N+1}$种放置石子的方案,最终口袋中石子数的和为多少。答案对 $10^9+7$ 取模。 $n\leq 2\times 10^5$ ,时限$\texttt{3s}$。 ------------ 先考虑若给定石子放置方式如何求出口袋中的石子数。 不难发现,只有同深度的石子可能相遇,且对于每个深度最多只有可能收到一枚石子。 于是,可以对每层的节点分别考虑。 记 $dep_u$ 为 $u$ 的深度, $siz_u$ 为 $u$ 子树内节点数,$f_{u,d}$ 为 $u$ 子树内深度为 $d$ 的点合并到 $u$ 后,使得 $u$ 为 $1$ 的概率(方案数与总方案数只比)。 (若直接维护方案数,则需要更复杂的乘法标记) 边界 : $f_{u,dep_u}=1/2$。 记 $u$ 的直接儿子为 $v_1,v_2,...v_m$ ,转移 : $$ f_{u,d}=\sum\limits_{i=1}^mf_{v_i,d}\prod_{j\neq i}(1-f_{v_j,d}) $$ 最终 $f_{0,\_}$ 的和乘以 $2^{n+1}$ 即为答案。 可以使用长链剖分维护,复杂度 $O(n)$。 ```cpp #include<algorithm> #include<cstdio> #include<vector> #define MaxN 200500 using namespace std; const int mod=1000000007,inv2=500000004; vector<int> g[MaxN]; int len[MaxN],dep[MaxN],siz[MaxN],mp[MaxN]; void pfs(int u) { siz[u]=1; for (int i=0,v;i<g[u].size();i++){ dep[v=g[u][i]]=dep[u]+1; pfs(v); siz[u]+=siz[v]; len[u]=max(len[u],len[v]+1); if (len[v]>len[mp[u]])mp[u]=v; } } int _o[MaxN<<1],*f[MaxN],*tf=_o,s[MaxN]; void dfs(int u,int fa) { f[u][dep[u]]=inv2; if (!mp[u])return ; f[mp[u]]=f[u]; dfs(mp[u],u); int td=0; for (int i=0,v;i<g[u].size();i++) if ((v=g[u][i])!=mp[u]){ f[v]=tf-dep[v];tf+=len[v]+1;dfs(v,u); td=max(td,len[v]); } for (int i=dep[u]+1;i<=dep[u]+td+1;i++)s[i]=1-f[u][i]; for (int i=0,v;i<g[u].size();i++) if ((v=g[u][i])!=mp[u]) for (int k=dep[v];k<=dep[v]+len[v];k++){ f[u][k]=(1ll*s[k]*f[v][k]+1ll*f[u][k]*(1-f[v][k]))%mod; s[k]=1ll*s[k]*(1-f[v][k])%mod; } } int n; int main() { scanf("%d",&n); for (int i=2,fa;i<=n+1;i++){ scanf("%d",&fa); g[fa+1].push_back(i); } len[0]=-1;pfs(1); f[1]=tf;tf+=len[1]+1;dfs(1,0); int ans=0; for (int i=0;i<=len[1];i++) ans=(ans+f[1][i])%mod; for (int i=1;i<=n+1;i++)ans=(ans<<1)%mod; printf("%d",(ans+mod)%mod); return 0; } ```