LOJ 6433 「PKUSC2018」最大前缀和——状压DP
题目:https://loj.ac/problem/6433
想到一个方案中没有被选的后缀满足 “该后缀的任一前缀和 <=0 ”。
于是令 dp[ S ] 表示选了点集 S ,满足任一前缀和 <=0 的方案。很好转移。
令 f[ S ] 表示选了点集 S ,且 S 整体就是最大前缀和的方案。
只会 3n 做出 f[ ] ,就是考虑容斥, \( f[s]=|s|! - \sum f[d]*dp[s^d] (sm[d]>=sm[s]) \) ,其中 sm[ s ] 表示点集 s 的权值和。
然后看了题解。发现 “ S 整体是最大前缀和 ” <==> “ S 的任一后缀和 >0 ” ,所以像做 dp[ ] 一样,从后往前放数字就能转移了!!!
还要训练思维。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; int Mx(int a,int b){return a>b?a:b;} const int N=25,M=(1<<20)+5,mod=998244353; int upt(int x){while(x>=mod)x-=mod;while(x<0)x+=mod;return x;} int n,a[N],dp[M],f[M]; ll sm[M]; int ct[M],bin[N],lg[M],jc[N]; namespace S1{ void solve(int cnt) { int ans=0;for(int i=1;i<=n;i++)ans=upt(ans+a[i]); for(int i=1;i<=n;i++)ans=(ll)ans*i%mod; if(!cnt||n==1){printf("%d\n",ans); return;}//n==1!! int rt,U=bin[n]-1; for(int i=1;i<=n;i++)if(a[i]<0){rt=i-1;break;}//i-1 for bin[] for(int s=0;s<U;s++)//s<U if((s&bin[rt])&&sm[s]<0) ans=upt(ans-(ll)jc[ct[U^s]]*jc[ct[s]-1]%mod*sm[s]%mod); printf("%d\n",ans); } } namespace S2{ int dp[M][85],mn[M],mx[M],fx=40; void init() { for(int s=1;s<bin[n];s++) for(int i=1;i<=n;i++) if(s&bin[i-1]) {if(a[i]>0)mx[s]+=a[i];else mn[s]+=a[i];} } void solve() { init(); dp[0][0]=1;int U=bin[n]-1; mn[0]=mx[0]=-40;/// //0 not 0+fx for 'none' can't be cal for(int s=0;s<U;s++) { int tp=U^s,d; while(tp) { d=tp&-tp; tp^=d; d|=s; for(int i=mn[s];i<=mx[s];i++) if(dp[s][i+fx]) { int j=Mx(i,sm[d])+fx; dp[d][j]=upt(dp[d][j]+dp[s][i+fx]); } } } int ans=0; for(int i=mn[U];i<=mx[U];i++) if(dp[U][i+fx]) ans=(ans+(ll)dp[U][i+fx]*i)%mod; printf("%d\n",upt(ans));//upt } } void init() { bin[0]=1; lg[1]=0; for(int i=1;i<=n;i++) bin[i]=bin[i-1]<<1,lg[bin[i]]=i; jc[0]=1;for(int i=1;i<=n;i++)jc[i]=(ll)jc[i-1]*i%mod; for(int s=1;s<bin[n];s++) { sm[s]=sm[s^(s&-s)]+a[lg[s&-s]+1]; ct[s]=ct[s^(s&-s)]+1; } } int main() { scanf("%d",&n); bool fg=0;int cnt=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); if(a[i]<-2||a[i]>2)fg=1; if(a[i]<0)cnt++; } init(); if(cnt<=1){S1::solve(cnt);return 0;} if(!fg){S2::solve();return 0;} dp[0]=1; for(int s=1;s<bin[n];s++) { if(sm[s]>0)continue; for(int i=1;i<=n;i++) if(s&bin[i-1]) { int d=s^bin[i-1]; if(dp[d])dp[s]=upt(dp[s]+dp[d]); } } f[0]=1; for(int s=1;s<bin[n];s++) for(int i=1;i<=n;i++) if(s&bin[i-1]) { int d=s^bin[i-1]; if(sm[d]>0||!d)f[s]=upt(f[s]+f[d]);//!d } int ans=0; for(int s=1,U=bin[n]-1;s<bin[n];s++) if(f[s]&&dp[U^s]) ans=(ans+sm[s]%mod*f[s]%mod*dp[U^s])%mod; printf("%d\n",upt(ans)); return 0; }