loj#6433. 「PKUSC2018」最大前缀和(状压dp)
Posted bztminamoto
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了loj#6433. 「PKUSC2018」最大前缀和(状压dp)相关的知识,希望对你有一定的参考价值。
今天(PKUWC)试机的题
看着边上的大佬们一个个(A)穿咱还是不会……
我们考虑枚举最大前缀和,如果一个前缀(1)到(p)是最大前缀和,那么(p)后面的所有前缀和都要小于(0)
于是我们设(sum_S)为子集(S)中所有元素的和,(f_S)为满足最大前缀和为(sum_S)的(S)的排列个数,那么我们可以枚举这个排列中位于第一个的数,只要剩下的数之和(sum_{S-{x}})大于(0),那么(f_S)就可以加上(f_{S-{x}})
然后设(g_S)为满足最大前缀和小于(0)的(S)的排列个数,和上面差不多,只要枚举位于最后的元素是哪个就行了
//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
inline int max(const R int &x,const R int &y){return x>y?x:y;}
inline int min(const R int &x,const R int &y){return x<y?x:y;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
R int res=1,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=(1<<20)+5,P=998244353;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
R int res=1;
for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);
return res;
}
int f[N],g[N],sum[N],a[N];
int n,res,lim;
int main(){
n=read(),lim=(1<<n)-1;
fp(i,0,n-1)a[1<<i]=read();
fp(i,1,lim)sum[i]=sum[i^(i&-i)]+a[i&-i];
g[0]=1;
fp(i,0,lim)if(sum[i]<=0){
R int j=i;
while(j)g[i]=add(g[i],g[i^(j&-j)]),j^=j&-j;
}
fp(i,0,n-1)f[1<<i]=1;
fp(i,0,lim){
if(sum[i]>0){
R int j=lim^i;
while(j)f[i|(j&-j)]=add(f[i|(j&-j)],f[i]),j^=j&-j;
}
// printf("%d %d
",i,f[i]);
res=add(res,1ll*f[i]*(sum[i]+P)%P*g[lim^i]%P);
}
printf("%d
",res);
return 0;
}
以上是关于loj#6433. 「PKUSC2018」最大前缀和(状压dp)的主要内容,如果未能解决你的问题,请参考以下文章