[CEOI2020] 星際迷航
阿新 • • 發佈:2022-03-04
一、題目
二、解法
考慮單向邊的意義,如果接上來的是必勝點,那麼顯然對勝負狀態沒有影響的。如果接上來的是必敗點,那麼如果原來的必敗點狀態會翻轉,否則還是必勝態。
但總之,我們只需要關心接上來的點的必勝點還是必敗點,接上來的圖是什麼樣子根本不重要。
那麼設 \(A_i\) 表示 \(i\) 個版本所有情況下的必敗點總數,\(B_i\) 表示 \(i\) 個版本所有情況下的必勝點總數,設 \(f_u\) 表示點 \(u\) 的初始狀態,\(g_u\) 表示有多少種連線方案(指在樹上接一個必敗點)可以讓 \(u\) 的初始狀態改變:
\[A_i\leftarrow A_{i-1}\cdot \sum_u([f_u=1]g_u+[f_u=0](n-g_u))+B_{i-1}\cdot n\cdot \sum_u[f_u=0] \]\[B_i\leftarrow B_{i-1}\cdot \sum_u([f_u=0]g_u+[f_u=1](n-g_u))+B_{i-1}\cdot n\cdot\sum_{u} [f_u=1] \]很容易用矩陣乘法優化,所以關鍵是求出 \(f,g\)
- 若 \(f_u=0\),則翻轉自己或者任意一個兒子都可以,\(g_u=1+\sum_{v}[f_v=1]\cdot g_v\)
- 若 \(f_u=1\) 且必敗的兒子只有一個,\(g_u=\sum_v[f_v=0]g_v\)
所以可以換根,時間複雜度 \(O(n+\log d)\)
#include <cstdio> #include <vector> using namespace std; const int M = 100005; const int MOD = 1e9+7; #define int long long int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,d,f[M],g[M],g0[M],g1[M],cnt[M];vector<int> G[M]; struct mat { int a[2][2]; mat() {a[0][0]=a[1][0]=a[0][1]=a[1][1]=0;} mat operator * (const mat &b) const { mat r; for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) r.a[i][k]=(r.a[i][k]+a[i][j]*b.a[j][k])%MOD; return r; } }A,F; void dfs1(int u,int fa) { cnt[u]=f[u]=g0[u]=g1[u]=g[u]=0; for(int v:G[u]) if(v^fa) { dfs1(v,u); if(!f[v]) cnt[u]++,g0[u]+=g[v]; else g1[u]+=g[v]; } f[u]=(cnt[u]>0); if(!f[u]) g[u]=g1[u]+1; else if(cnt[u]==1) g[u]=g0[u]; } void dfs2(int u,int fa) { cnt[u]=f[u]=g0[u]=g1[u]=g[u]=0; for(int v:G[u]) { if(!f[v]) cnt[u]++,g0[u]+=g[v]; else g1[u]+=g[v]; } f[u]=(cnt[u]>0); if(!f[u]) g[u]=g1[u]+1; else if(cnt[u]==1) g[u]=g0[u]; // if(f[u]==1) { F.a[0][1]++; A.a[0][0]=(A.a[0][0]+g[u])%MOD; A.a[0][1]=(A.a[0][1]+n-g[u])%MOD; A.a[1][1]=(A.a[1][1]+n)%MOD; } else { F.a[0][0]++; A.a[0][1]=(A.a[0][1]+g[u])%MOD; A.a[0][0]=(A.a[0][0]+n-g[u])%MOD; A.a[1][0]=(A.a[1][0]+n)%MOD; } // int tf=f[u],tg=g[u],tc=cnt[u]; for(int v:G[u]) if(v^fa) { if(!f[v]) { if(cnt[u]==1) cnt[u]=0,g[u]=g1[u]+1,f[u]=0; else//f[u]=1; { cnt[u]--; g[u]=(cnt[u]==1)?g0[u]-g[v]:0; } } else if(!f[u]) g[u]=g1[u]-g[v]+1; dfs2(v,u); f[u]=tf;g[u]=tg;cnt[u]=tc; } } signed main() { n=read();d=read()-1; for(int i=1;i<n;i++) { int u=read(),v=read(); G[u].push_back(v); G[v].push_back(u); } dfs1(1,0);dfs2(1,0); while(d>0) {if(d&1) F=F*A;A=A*A;d>>=1;} int ans=g[1]*F.a[0][0]%MOD; if(f[1]) ans=(n*(F.a[0][0]+F.a[0][1])-ans)%MOD; printf("%lld\n",(ans+MOD)%MOD); }