题目大意:求给定的 $n$ 个数的所有排列的最大前缀和(不能为空)之和对 $10^9+7$ 取模的值。
$1le nle 20,1lesum|a_i|le 10^9$。
神级DP。杂题选讲的神级毒瘤讲题人CDW讲的。
考虑一个集合 $S$ 能作为最大前缀和出现的方案数。(即贡献系数)
发现前 $|S|$ 个数满足最大前缀和是整个序列,后 $n-|S|$ 个数满足最大前缀和 $<0$。(虽然 $le 0$ 也行,但为了避免重复统计就要 $<0$)
设 $f[S]$ 为在 $S$ 的所有排列中,最大前缀和 $<0$ 的个数。
设 $g[S]$ 为在 $S$ 的所有排列中,最大前缀和 $=sum[S]$ 的个数。($sum$ 是和)
$f[S]=egin{cases}0&sum[S]ge 0\ sumlimits_{iin S}f[S-{i}]&sum[S]<0end{cases}$
初始 $f[0]=1$。
解释一下,如果 $sum[S]ge 0$,那么最大前缀和不会小于 $0$。否则枚举最后一个数,当且仅当前面的最大前缀和 $<0$ (或者前面没有数,所以 $f[0]=1$)且 $sum[S]<0$ 时才可以。第二个条件已经保证满足了。
$g[S] ightarrow g[S+(1<<i)](sum[S]ge 0,i otin S)$
初始 $g[{i}]=1$。
解释一下,考虑从已有状态扩展,枚举在 $S$ 前加一个数 $i$,当且仅当 $S$ 最大前缀和是自己时,新序列最大前缀和才是自己。、
答案为 $sum sum[S]g[S]f[U-S]$。
时间复杂度 $O(n2^n)$。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=998244353; #define FOR(i,a,b) for(int i=(a);i<=(b);i++) #define ROF(i,a,b) for(int i=(a);i>=(b);i--) #define MEM(x,v) memset(x,v,sizeof(x)) inline int read(){ char ch=getchar();int x=0,f=0; while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return f?-x:x; } int n,a[22],ans,f[1111111],g[1111111]; ll S[1111111]; int main(){ n=read(); FOR(i,0,n-1) a[i]=read(),g[1<<i]=1; FOR(i,0,(1<<n)-1) FOR(j,0,n-1) if((i>>j)&1) S[i]+=a[j]; f[0]=1; FOR(i,0,(1<<n)-1){ if(S[i]>=0){FOR(j,0,n-1) if(!((i>>j)&1)) g[i|(1<<j)]=(g[i|(1<<j)]+g[i])%mod;} else FOR(j,0,n-1) if((i>>j)&1) f[i]=(f[i]+f[i^(1<<j)])%mod; } FOR(i,0,(1<<n)-1) ans=(ans+1ll*(S[i]+mod)%mod*g[i]%mod*f[((1<<n)-1)^i])%mod; printf("%d ",ans); }