AtCoder AGC039F Min Product Sum (容斥原理、组合计数、DP)
題目鏈接
https://atcoder.jp/contests/agc039/tasks/agc039_f
題解
又是很簡單的F題我不會。。。
考慮先給每行每列欽定一個最小值\(a_i,b_j\),并假設每行每列的最小值是這個數,且每行每列只需要放\(\ge\)這個數的數即可,那么這種情況的價值是\(\prod^n_{i=1}\prod^m_{j=1}\min(a_i,b_j)\), 方案數是\(\prod^n_{i=1}\prod^m_{j=1}(n+1-\max(a_i,b_j))\)
然后我們需要把最小值的限制容斥掉,也就是枚舉若干行若干列容斥掉(限制\(+1\)同時系數乘以\(-1\))。
這樣的話直接暴力DP就可以解決。設\(f[k][i][j]\)表示當前用\([1,k]\)中的數填滿了\(i\)行\(j\)列。轉移可以直接枚舉不被容斥的行數、不被容斥的列數、容斥的行數、容斥的列數,乘上貢獻系數,得到了一個多項式時間復雜度的算法。
但是我們發現這樣轉移顯然很浪費,我們可以把四個變量同時枚舉改成分四個階段依次枚舉,這樣轉移時間復雜度降到了\(O(n)\).(注意因為要保證從小到大填數,所以必須先枚舉不被容斥再枚舉被容斥)
不過這題還挺卡常的……需要\(O(n^3)\)預處理一下轉移系數,詳見代碼
時間復雜度\(O(n^4)\)
orz myh
代碼
#include<bits/stdc++.h> #define llong long long #define mkpr make_pair #define riterator reverse_iterator using namespace std;inline int read() {int x = 0,f = 1; char ch = getchar();for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}return x*f; }const int N = 100; int P; llong pw[N+3][N*N+3]; llong comb[N+3][N+3]; llong f[2][N+3][N+3]; llong trans[N+3][N+3]; int n,m,p;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; }void initmath() {for(int i=0; i<=N; i++){pw[i][0] = 1ll; for(int j=1; j<=N*N; j++) pw[i][j] = pw[i][j-1]*i%P;}comb[0][0] = 1ll;for(int i=1; i<=N; i++){comb[i][0] = comb[i][i] = 1ll;for(int j=1; j<i; j++) comb[i][j] = (comb[i-1][j]+comb[i-1][j-1])%P;} }llong updsum(llong &x,llong y) {x = x+y>=P?x+y-P:x+y;}int main() {scanf("%d%d%d%lld",&n,&m,&p,&P);initmath();int curk = 0; f[0][0][0] = 1ll;for(int k=1; k<=p; k++){curk^=1; memset(f[curk],0,sizeof(f[curk]));for(int j=0; j<=m; j++) for(int ii=0; ii<=n; ii++) trans[j][ii] = pw[k][ii*(m-j)]%P*pw[p-k+1][ii*j]%P;for(int i=0; i<=n; i++){for(int j=0; j<=m; j++){llong x = f[curk^1][i][j]; if(!x) continue;for(int ii=0; ii+i<=n; ii++){updsum(f[curk][i+ii][j],x*comb[i+ii][i]%P*trans[j][ii]%P);}}}curk^=1; memset(f[curk],0,sizeof(f[curk]));for(int i=0; i<=n; i++) for(int jj=0; jj<=m; jj++) trans[i][jj] = pw[k][jj*(n-i)]%P*pw[p-k+1][jj*i]%P;for(int i=0; i<=n; i++){for(int j=0; j<=m; j++){llong x = f[curk^1][i][j]; if(!x) continue;for(int jj=0; jj+j<=m; jj++){updsum(f[curk][i][j+jj],x*comb[j+jj][j]%P*trans[i][jj]%P);}}}curk^=1; memset(f[curk],0,sizeof(f[curk]));for(int j=0; j<=m; j++) for(int ii=0; ii<=n; ii++) trans[j][ii] = pw[k][ii*(m-j)]%P*pw[p-k][ii*j]%P;for(int i=0; i<=n; i++){for(int j=0; j<=m; j++){llong x = f[curk^1][i][j]; if(!x) continue;for(int ii=0; ii+i<=n; ii++){llong y = x*comb[i+ii][i]%P*trans[j][ii]%P;updsum(f[curk][i+ii][j],ii&1?P-y:y);}}}curk^=1; memset(f[curk],0,sizeof(f[curk]));for(int i=0; i<=n; i++) for(int jj=0; jj<=m; jj++) trans[i][jj] = pw[k][jj*(n-i)]%P*pw[p-k][i*jj]%P;for(int i=0; i<=n; i++){for(int j=0; j<=m; j++){llong x = f[curk^1][i][j]; if(!x) continue;for(int jj=0; jj+j<=m; jj++){llong y = x*comb[j+jj][j]%P*trans[i][jj]%P;updsum(f[curk][i][j+jj],jj&1?P-y:y);}}}}printf("%lld\n",f[curk][n][m]);return 0; }總結
以上是生活随笔為你收集整理的AtCoder AGC039F Min Product Sum (容斥原理、组合计数、DP)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AtCoder AGC035E Deve
- 下一篇: AtCoder AGC029E Wand