o_o 当前时间是:

4:02:10 AM

 

PKUSC2018 最大前缀和

题意

给一个长度为 n 的整数序列,求其 n! 种排列方式的最大前缀和(不能为空)的总和。

  • n20

解法

设全集为 U,考虑枚举作为最大前缀和的子集 S。那么要求的就是 S 排列后严格最大前缀和在最后一个元素取到和方案数和 US 排列后每个前缀和都小于等于 0 的方案数。

后者可以枚举最后一个数填什么转移,前者可以枚举上一个取到严格最大前缀和的子集是哪一个转移,需要注意处理只选一个数的边界情况,复杂度是 O(3n+2nn) 的。

一个精妙的 trick 是考虑 S 在最后一个位置取到严格最大前缀和相当于把它 reverse 之后前缀和大于 0,这样就可以类比每个前缀和都小于等于 0 做了,同样需要处理只有一个数的情况。

code

代码的边界很不好写。

#include<bits/stdc++.h>
using namespace std;
using E=long long;
const E mod=998244353;
int n;
vector<E> a;
int main(){
cin>>n;
a.resize(n);
for(int i=0; i<n; i++){
cin>>a[i];
}
vector<E> f(1<<n),g(1<<n),sum(1<<n);
f[0]=g[0]=1;
for(int msk=1; msk<(1<<n); msk++){
for(int i=0; i<n; i++) if(msk>>i&1) sum[msk]+=a[i];
if(sum[msk]<=0) for(int i=0; i<n; i++){
if((msk>>i&1)==0) continue;
g[msk]=(g[msk]+g[msk^(1<<i)])%mod;
}
if((msk&-msk)==msk) f[msk]=1;
else for(int i=0; i<n; i++){
if(msk>>i&1) if(sum[msk^(1<<i)]>0) f[msk]=(f[msk]+f[msk^(1<<i)])%mod;
}
}
E ans=0;
for(int msk=1; msk<(1<<n); msk++){
sum[msk]%=mod;
ans=(ans+f[msk]*g[((1<<n)-1)^msk]%mod*sum[msk])%mod;
//cerr<<msk<<' '<<f[msk]<<' '<<ans<<endl;
}
cout<<(ans%mod+mod)%mod;
return 0;
}
posted @   zzafanti  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示