【十二省联考2019】皮配【分部dp】
題意:有 nnn 個學校隸屬于 ccc 個城市,每個學校有 sis_isi? 個人。把它們放入一個 2×22\times 22×2 的格子中,要求同一學校的必須放在同一個格子,同一城市的必須放在同一行,并給出兩行兩列分別最多能放的人數C0,C1,D0,D1C_0,C_1,D_0,D_1C0?,C1?,D0?,D1?。此外還有 kkk 條限制,形如某個學校的人不能放入某個特定的格子中,每個學校最多只有一種限制。求方案數 模 998244353998244353998244353。
n,c≤103,C0,C1,D0,D1≤2500,k≤30,si≤10n,c\leq 10^3,C_0,C_1,D_0,D_1\leq 2500,k\leq 30,s_i\leq 10n,c≤103,C0?,C1?,D0?,D1?≤2500,k≤30,si?≤10,數據組數 T=5T=5T=5
首先考慮暴力 dp,發現確定第一行、第一列后完整方案就確定了。所以可以設 f(x,y)f(x,y)f(x,y) 表示第一行放了 xxx 個數,第一列放了 yyy 個數,直接轉移即可。計算答案時 xxx 和 yyy 的上下界都可以確定出來,這樣就有 50pts 的好成績。
然后發現對于沒有限制的情況,行和列是分別獨立的,所以分別設 A(x),B(x)A(x),B(x)A(x),B(x) 為第一行、第一列選了 xxx 個的方案數,前者用城市總人數轉移,后者用學校人數轉移,然后乘起來就可以了。單獨寫就有 50pts,結合前面可以拿到 70pts。
考慮這個方法不好擴展,但發現有限制的城市不超過 kkk 個,所以可以從“降低了數據范圍的方向”來考慮。
開始想的是 dp 的時候記錄兩列受限制的人數,然而狀態數爆炸。
發現城市數很少并沒什么用,因為一個城市里面即使只有一個受限制的學校,你整個城市都只能大暴力,
智商分割線
我們稱一個城市受限當且僅當至少一個該城市的學校受限。考慮繼續挖掘受限的城市中不受限的學校的性質。
冷靜分析可以發現,對于一個受限的城市,確定了受限的學校選擇了哪一行后,不受限的學校選哪一行就確定了,只需要決定選哪一列然后根據最終方案隨機應變就可以了。也就是說這部分拿去更新 B(x)B(x)B(x) 就可以不管了。
對于最終受限的學校只有 303030 個,直接用第一個方法大暴力就可以了。要注意的是需要先決定每一個城市選了哪一行,可以對每個城市把狀態分為兩部分,分別為選擇第一行和第二行的 dp 值。然后把兩部分直接加起來,在這里把第一行該城市的人數減掉。
注意 dp 順序,必須依次枚舉城市更新所有信息,否則會算重。
然后把這個暴力 dp 求二維前綴和,枚舉前面的 A(x),B(x)A(x),B(x)A(x),B(x) 算貢獻即可。
顯然復雜度瓶頸在第三步,復雜度為 O(kM2)O(kM^2)O(kM2),非常卡。
注意到這個 dp 是從頭開始算的,第二維不會超過 ksiks_iksi?,就可以優化到 O(k2siM)O(k^2s_iM)O(k2si?M)
總復雜度 O(TM(N+k2si))O(TM(N+k^2s_i))O(TM(N+k2si?))
有點卡常,不過不用 memset 應該問題不大
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <vector> #define re register using namespace std; typedef long long ll; const int MOD=998244353; inline int mod(int x){return x+=(x>>31)&MOD;} inline int add(const int& x,const int& y){return mod(x+y-MOD);} inline int dec(const int& x,const int& y){return mod(x-y);} inline int read() {int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans; } vector<int> lis[1005],fr,hfr; int n,c,k,C[2],D[2],b[1005],s[1005],p[1005],cnt[1005],M; int A[2505],B[2505],g[2505],f[2505][2505],t0[2505][2505],t1[2505][2505],tmp0[2505][2505],tmp1[2505][2505],siz[1005]; inline void clear() {for (int i=1;i<=c;i++) lis[i].clear();memset(p,-1,sizeof(p));memset(cnt,0,sizeof(cnt));fr.clear(),hfr.clear();memset(A,0,sizeof(A));memset(B,0,sizeof(B));memset(g,0,sizeof(g));memset(f,0,sizeof(f));memset(siz,0,sizeof(siz));A[0]=B[0]=g[0]=f[0][0]=1; } int main() {freopen("test.in","r",stdin);for (int T=read();T;T--){n=read(),c=read(),C[0]=read(),C[1]=read(),D[0]=read(),D[1]=read();M=max(max(C[0],C[1]),max(D[0],D[1]));clear();int tot=0;for (int i=1;i<=n;i++) b[i]=read(),s[i]=read(),siz[b[i]]+=s[i],tot+=s[i];k=read();int MAX=min(M,k*10);while (k--){int i=read();p[i]=read();++cnt[b[i]];}if (C[0]+C[1]<tot||D[0]+D[1]<tot) {puts("0");continue;}for (int i=1;i<=n;i++)if (!cnt[b[i]]) fr.push_back(i);else if (p[i]==-1) hfr.push_back(i);else lis[b[i]].push_back(i);for (int i=1;i<=c;i++)if (!cnt[i]&&siz[i])for (int j=M;j>=siz[i];j--)A[j]=add(A[j],A[j-siz[i]]);for (int i=0;i<(int)fr.size();i++){int v=s[fr[i]];for (int j=M;j>=v;j--) B[j]=add(B[j],B[j-v]);}for (int i=0;i<(int)hfr.size();i++){int v=s[hfr[i]];for (int j=M;j>=v;j--) B[j]=add(B[j],B[j-v]);}for (re int i=1;i<=c;i++){if (!cnt[i]) continue;for (re int j=0;j<=M;j++)for (re int k=0;k<=MAX;k++)t0[j][k]=t1[j][k]=f[j][k];for (re int l=0;l<cnt[i];l++){re int v=s[lis[i][l]],lim=p[lis[i][l]];for (re int j=0;j<=M;j++)for (re int k=0;k<=MAX;k++)tmp0[j][k]=tmp1[j][k]=0;for (re int j=0;j<=M;j++)for (re int k=0;k<=MAX;k++){if (lim!=0&&k>=v) tmp0[j][k]=add(tmp0[j][k],t0[j][k-v]);if (lim!=1) tmp0[j][k]=add(tmp0[j][k],t0[j][k]);if (lim!=2&&k>=v) tmp1[j][k]=add(tmp1[j][k],t1[j][k-v]);if (lim!=3) tmp1[j][k]=add(tmp1[j][k],t1[j][k]);}for (re int j=0;j<=M;j++)for (re int k=0;k<=MAX;k++)t0[j][k]=tmp0[j][k],t1[j][k]=tmp1[j][k]; }for (int j=0;j<=M;j++)for (int k=0;k<=MAX;k++)f[j][k]=add(j>=siz[i]? t0[j-siz[i]][k]:0,t1[j][k]);}for (int i=0;i<=M;i++)for (int j=0;j<=M;j++){if (i) f[i][j]=add(f[i][j],f[i-1][j]);if (j) f[i][j]=add(f[i][j],f[i][j-1]);if (i&&j) f[i][j]=dec(f[i][j],f[i-1][j-1]);}int res=0;for (int i=0;i<=C[0];i++)for (int j=0;j<=D[0];j++){if (!A[i]||!B[j]) continue;int xl=max(0,tot-C[1]-i),xr=C[0]-i;int yl=max(0,tot-D[1]-j),yr=D[0]-j;int ans=f[xr][yr];if (xl) ans=dec(ans,f[xl-1][yr]);if (yl) ans=dec(ans,f[xr][yl-1]);if (xl&&yl) ans=add(ans,f[xl-1][yl-1]);res=(res+(ll)ans*A[i]%MOD*B[j])%MOD;}printf("%d\n",res);}return 0; }總結
以上是生活随笔為你收集整理的【十二省联考2019】皮配【分部dp】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分分钟给你的电脑免费加个TB级硬盘
- 下一篇: 【THUSC 2017】如果奇迹有颜色【