UOJ #214 合唱队形 (概率期望计数、DP、Min-Max容斥)
UOJ #214 合唱隊形 (概率期望計數、DP、Min-Max容斥)
9個月的心頭大恨終于切掉了!!!!
非常好的一道題,不知為何uoj上被點了70個差評。
題目鏈接: http://uoj.ac/problem/214
題目大意: 請自行閱讀。
題解:
官方題解講得相當清楚,這里補充一下自己的一些理解。
首先來看\(O(2^{n-m}\times poly(n,m))\)的做法。
一種理解方式是官方題解。
設\(s\)為總共的課程個數(\(n\)個字符串的總長度),\(p(S)\)表示結尾位置為集合\(S\)的串全部匹配一共需要完成多少個不同的課程。設\(f(t)\)表示\(t\)時刻整個過程沒有終止的概率,\(prob(S,t)\)表示\(S\)集合結尾的串在\(t\)時刻或者之前已經完成匹配的概率。\(ans\)為答案。
\[ans=\sum^{+\inf}_{t=0} f(t)=\sum^{+\inf}_{t=0} \sum_{S, |S|\ge 0}(-1)^{|S|} prob(S,t)=\sum^{+\inf}_{t=0} \sum_{S,|S|\ge 0}(-1)^{|S|} \sum^{f(S)}_{i=0} (-1)^i{f(S)\choose i}(\frac{s-i}{s})^t\\ =\sum^{+\inf}_{t=0} \sum_{S,|S|\ge 0}(-1)^{|S|} \sum^{f(S)}_{i=1} (-1)^i{f(S)\choose i}(\frac{s-i}{s})^t\\ =\sum_{S,|S|\ge 0}(-1)^{|S|} \sum^{f(S)}_{i=0} (-1)^i{f(s)\choose i}\sum^{\inf}_{t=0}(\frac{s-i}{s})^t=\sum_{S,|S|\ge 0}(-1)^{|S|} \sum^{f(S)}_{i=0} (-1)^i{f(S)\choose i} \frac{s}{i}\]
計算即可。
其實還有另外一種理解方式。我們要求的是所有串匹配時間最小值的期望,根據Min-Max容斥,我們可以通過枚舉子集轉化成對每個子集求該子集內串匹配時間最大值的期望,然后轉化成每一時刻沒結束的概率。
然后來看更難的\(O(2^m\times poly(n,m))\)做法。
對于所有\(p(S)\)相同的\(S\), 其對答案的貢獻沒有區別。
因此對于所有\(k\),考慮計算\(p(S)=k\)的\(S\)的個數。
然后直接dp即可。
代碼錯誤記錄: 注意solution1如果\(n-m=16\)的話數組要開到\(2^{17}\).
代碼
#include<cstdio> #include<cstdlib> #include<cstring> #define llong long long using namespace std;const int N = 30; const int S = 26; const int P = 998244353; bool ok[N+3]; int f[N+3][S+3]; char str[N+3]; char a[N+3]; llong fact[2000003],finv[2000003],inv[2000003]; int cnt[(1<<17)+3]; int n,m,s;llong quickpow(llong x,llong y) {llong cur = x,ret = 1ll;for(int i=0; y; i++){if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur%P;}cur = cur*cur%P;}return ret; } llong comb(llong x,llong y) {return x<0 || y<0 || x<y ? 0ll : fact[x]*finv[y]%P*finv[x-y]%P;}namespace Solution1 {int ff[N+3][S+3];llong ans;void solve(){ans = 0ll;for(int i=1; i<(1<<(n-m+1)); i++){llong cur = 0ll; bool gg = false;for(int j=0; j<=n-m; j++){if(i&(1<<j)){for(int k=0; k<m; k++){ff[j+k][a[k]-96] = true;if(f[j+k][a[k]-96]==false) {gg = true; break;}}if(gg==true) break;}}if(gg){for(int j=0; j<n; j++){for(int k=1; k<=S; k++){ff[j][k] = false;}}continue;}int num = 0;for(int j=0; j<n; j++){for(int k=1; k<=S; k++){if(ff[j][k]==true) {num++;}}}for(int j=1; j<=num; j++){llong tmp = (llong)s*inv[j]%P*comb(num,j)%P;if(!(j&1)) {cur = (cur+tmp)%P;}else {cur = (cur-tmp+P)%P;}}for(int j=0; j<n; j++){for(int k=1; k<=S; k++){ff[j][k] = false;}}if(cnt[i]&1) {ans = (ans-cur+P)%P;}else {ans = (ans+cur+P)%P;}}printf("%lld\n",ans);} }namespace Solution2 {#define U ((1<<m)-1)const int M = 14;llong dp[N+3][(1<<M)+3][N*M+3];bool ff[N+3][M+3];void update(llong &x,llong y) {x = (x+y)%P;}void solve(){llong ans = 0ll;dp[m-1][0][0] = 1ll;for(int i=m-1; i<n; i++){for(int j=0; j<(1<<m); j++){bool okk = true;for(int k=0; k<m; k++) if((j&(1<<k)) && ok[i-k-1]==false) {okk = false; break;}if(!okk) continue;int p = 0;for(int k=0; k<m; k++){if(j&(1<<k)){for(int l=0; l<m; l++){ff[i-k-1-m+1+l][a[l]-96] = true;}}}for(int l=0; l<m; l++){if(ff[i-m+1+l][a[l]-96]==false){p++;}}for(int k=0; k<m; k++){if(j&(1<<k)){for(int l=0; l<m; l++){ff[i-k-1-m+1+l][a[l]-96] = false;}}}for(int k=0; k<=n*m; k++){if(dp[i][j][k]==0) continue;update(dp[i+1][(j<<1)&U][k],dp[i][j][k]);update(dp[i+1][(j<<1|1)&U][k+p],P-dp[i][j][k]);dp[i][j][k] = 0ll;}}}for(int i=1; i<=n*m; i++){llong coe = 0ll;for(int j=1; j<=i; j++){llong tmp = s*inv[j]%P*comb(i,j)%P;if(j&1) {coe = (coe-tmp+P)%P;}else {coe = (coe+tmp)%P;}}llong sum = 0ll;for(int j=0; j<(1<<m); j++){sum = (sum+dp[n][j][i])%P;dp[n][j][i] = 0ll;}ans = (ans+sum*coe)%P;}printf("%lld\n",ans);} }int main() {fact[0] = 1ll; for(int i=1; i<=2000000; i++) fact[i] = fact[i-1]*i%P;finv[2000000] = quickpow(fact[2000000],P-2); for(int i=1999999; i>=0; i--) finv[i] = finv[i+1]*(i+1)%P;for(int i=1; i<=2000000; i++) inv[i] = finv[i]*fact[i-1]%P;cnt[0] = 0; for(int i=1; i<(1<<17); i++) cnt[i] = cnt[i>>1]+(i&1);int T; scanf("%d",&T);while(T--){scanf("%d%d",&n,&m); s = 0;for(int i=0; i<n; i++){scanf("%s",str); int l = strlen(str);for(int j=0; j<l; j++){f[i][str[j]-96] = true; s++;}}scanf("%s",a); bool exist = false;for(int i=m-1; i<n; i++){ok[i] = true;for(int j=0; j<m; j++){if(f[i-m+1+j][a[j]-96]==false) {ok[i] = false; break;}}if(ok[i]) {exist = true;}}if(exist==false) {printf("-1\n");}else{if(n-m<=16){Solution1::solve();}else{Solution2::solve();}}for(int i=0; i<n; i++){for(int j=1; j<=S; j++) f[i][j] = false;ok[i] = false;}}return 0; } 發表于 2019-06-15 11:55 suncongbo 閱讀(...) 評論(...) 編輯 收藏 刷新評論刷新頁面返回頂部總結
以上是生活随笔為你收集整理的UOJ #214 合唱队形 (概率期望计数、DP、Min-Max容斥)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UOJ #214 [UNR #1]合唱队
- 下一篇: UOJ #219 BZOJ 4650 l