「PKUSC2018」最大字首和
阿新 • • 發佈:2019-01-04
狀壓好題啊。
一眼看出時間複雜度 \(O(n2^n)\),然後開始想正解。
然後設 \(dp_i\) 為字首狀態為 \(i\) 時的方案數,所以這時候 \(sum_i\) 一定是單峰的。
可以推匯出:
\[(1\leq j<i)\ sum_i\geq sum_j\Rightarrow sum_i-sum_j\geq 0\]
\[(i<j\leq n)\ sum_j<sum_i\Rightarrow sum_j-sum_i<0\]
那麼我們相當於求兩個序列拼湊起來,一個序列字首和除第一項始終 \(\geq 0\)(因為 \(sum_i-sum_1\) 不包括第一項,但是 \(sum_n-sum_i\)
時間複雜度 \(O(n2^n)\)
\(Code\ Below:\)
#include <bits/stdc++.h> #define ll long long using namespace std; const int p=998244353; int n,lim,a[20],sum[1<<20],f[1<<20],g[1<<20]; int main() { scanf("%d",&n);lim=(1<<n)-1; for(int i=0;i<n;i++) scanf("%d",&a[i]); for(int i=1;i<=lim;i++) for(int j=0;j<n;j++) if(i&(1<<j)){ sum[i]=sum[i^(1<<j)]+a[j]; break; } f[0]=g[0]=1; for(int i=1;i<=lim;i++) if(sum[i]<0) for(int j=0;j<n;j++) if((i&(1<<j))&&(sum[i^(1<<j)]<0||!(i^(1<<j)))) f[i]=(f[i]+f[i^(1<<j)])%p; for(int i=1;i<=lim;i++) for(int j=0;j<n;j++) if((i&(1<<j))&&(sum[i^(1<<j)]>=0||!(i^(1<<j)))) g[i]=(g[i]+g[i^(1<<j)])%p; int ans=0; for(int i=1;i<=lim;i++) ans=(ans+(ll)g[i]*f[lim^i]%p*(sum[i]+p)%p)%p; printf("%d\n",ans); return 0; }