AT_xmascon20_f 题解

· · 题解

首先 exchange argument 一下可以知道确定扔掉哪些的时候,一定会按 $b_i$ 单调递减的顺序去做菜。 考虑如何对于给定的 $a_i$ 计算答案($b_i$ 排序好了)。先二分,然后从前往后扫,将 $a_i$ 扔进一个 pq 里面。如果目前的前缀和加上 $b_i$ 比二分的值大了,那就弹出 pq 中最大的并不选他。容易理解这么做的正确性。 考虑经典 trick dp 维护 pq。枚举二分的值,设计 $dp_{i,j,k,l,0/1/2}$ 表示看到 $i$,考虑所有我们打算不选的东西之后目前前缀和为 $j$,不选了 $k$ 个,目前选不选的分界线为 $l$。$0/1/2$ 为辅助维度,分别表示目前没有见到任何 $l$,见到了 $l$ 且全选了,见到了 $l$ 选了且有一段后缀没选。 注意这里我们不需要记录 pq 里面剩了多少数,因为是否弹出只跟是否会爆有关,就不用在 pq 清空的时候将 $l$ 变大了,每次判断 $j+b_i+l$ 如果大于二分的值,那么相当于顶满了,就可以将 $l$ 转移到更大的位置。注意最后一维 $1$ 可以转移到 $\geq l$ 而 $2$ 可以转移到 $>l$。 一个小问题是对于不删的位置,我们还需要保证 $j+b_i$ 小于等于二分的值,在这类转移的时候判一下即可。 这么做复杂度是 $O(n^4v^3)$ 的,感觉已经能过了。 考虑优化,注意到我们只在乎(二分的值不妨设为 $lim$)$lim-j$ 的值,让 $nv+\max b_i$ 次 dp 并行即可,总复杂度 $O(n^3v^2)$。 统计答案是容易的,不再阐述。 ``` #include <bits/stdc++.h> #define mid ((l+r)>>1) #define lowbit(i) (i&(-i)) using namespace std; const int mod=998244353; int qp(int a,int b){ int ans=1; while(b){ if(b&1) ans=1ll*ans*a%mod; a=1ll*a*a%mod; b>>=1; } return ans; } inline void add(int &i,int j){ i+=j; if(i>=mod) i-=mod; } inline int addv(int i,int j){ i+=j; if(i>=mod) i-=mod; return i; } int b[35]; int dp[31][1205][31][22][3]; int tmp[1205][31][22][3]; signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); int n,v; cin>>n>>v; for(int i=1;i<=n;i++) cin>>b[i]; sort(b+1,b+n+1); reverse(b+1,b+n+1); for(int t=0;t<=1200;t++) for(int j=1;j<=v+1;j++) dp[0][t][0][j][0]=1; for(int i=1;i<=n;i++){ memset(tmp,0,sizeof(tmp)); for(int j=0;j<=1200;j++){ for(int k=0;k<i;k++){ for(int l=1;l<=v+1;l++){ if(!dp[i-1][j][k][l][0]&&!dp[i-1][j][k][l][1]&&!dp[i-1][j][k][l][2]) continue; { //choose <l add(tmp[max(0,j-l+1)][k][l][0],dp[i-1][j][k][l][0]); add(tmp[max(0,j-l+1)][k][l][1],dp[i-1][j][k][l][1]); add(tmp[max(0,j-l+1)][k][l][2],dp[i-1][j][k][l][2]); add(tmp[j][k][l][0],mod-dp[i-1][j][k][l][0]); add(tmp[j][k][l][1],mod-dp[i-1][j][k][l][1]); add(tmp[j][k][l][2],mod-dp[i-1][j][k][l][2]); } if(l!=v+1){ //choose =l add(dp[i][j][k+1][l][1],dp[i-1][j][k][l][0]); add(dp[i][j][k+1][l][1],dp[i-1][j][k][l][1]); if(j-l>=0&&j-l>=b[i]){ add(dp[i][j-l][k][l][2],dp[i-1][j][k][l][1]); add(dp[i][j-l][k][l][2],dp[i-1][j][k][l][2]); } } if(l!=v+1){ //choose >l add(dp[i][j][k+1][l][0],1ll*dp[i-1][j][k][l][0]*(v-l)%mod); add(dp[i][j][k+1][l][1],1ll*dp[i-1][j][k][l][1]*(v-l)%mod); add(dp[i][j][k+1][l][2],1ll*dp[i-1][j][k][l][2]*(v-l)%mod); } } } } for(int j=0;j<=1200;j++){ for(int k=0;k<i;k++){ for(int l=1;l<=v+1;l++){ if(j){ add(tmp[j][k][l][0],tmp[j-1][k][l][0]); add(tmp[j][k][l][1],tmp[j-1][k][l][1]); add(tmp[j][k][l][2],tmp[j-1][k][l][2]); } if(j>=b[i]){ add(dp[i][j][k][l][0],tmp[j][k][l][0]); add(dp[i][j][k][l][1],tmp[j][k][l][1]); add(dp[i][j][k][l][2],tmp[j][k][l][2]); } } } } memset(tmp,0,sizeof(tmp)); for(int j=0;j<=1200;j++){ for(int k=0;k<=i;k++){ for(int l=1;l<=v+1;l++){ if(b[i]+l>j){ add(tmp[j][k][l][0],dp[i][j][k][l][1]); add(tmp[j][k][l+1][0],dp[i][j][k][l][2]); dp[i][j][k][l][1]=0; dp[i][j][k][l][2]=0; } } } } for(int j=0;j<=1200;j++){ for(int k=0;k<=i;k++){ for(int l=1;l<=v+1;l++){ add(tmp[j][k][l][0],tmp[j][k][l-1][0]); add(dp[i][j][k][l][0],tmp[j][k][l][0]); } } } } for(int i=1;i<=n;i++){ int j=n-i,ans=0; for(int k=0;k<=1200;k++){ for(int nd=j+1;nd<=n;nd++){ add(ans,dp[n][k][nd][v+1][0]); } } cout<<ans<<" \n"[i==n]; } return 0; } ```