「PKUSC2018」最大前缀和 [DP?]

「PKUSC2018」最大前缀和

Tags: DP 状压


「PKUSC2018」最大前缀和

题意

求对于a[]的所有排列的的最大前缀和的和对998244353取模的值。

分析

其实就是一个计数问题?和概率期望没有什么关系。
然后考虑状压,然后某个状态表示的是选择了当前那么多东西之后的最大前缀和之和。
考虑选择或者不选择后面的一段?
但是这样还是有点问题。

只需要考虑如果当前的s被作为最大的前缀和的情况数量。
那么其实考虑有多少个剩下元素组成的最大前缀和<0

记dp1为以s为最大前缀和的串的个数。(前面没有比它更大的)
记dp2为s有多少种取法使得最大前缀和<0

对于dp1

d p 1 [ n s ] = s , s | ( 1 << i ) = n s d p 1 [ s ] ( i f s u m [ n s ] > 0 ) + 1 ( i f s = 0 )

对于dp2
d p 2 [ n s ] = { s , s | ( 1 << i ) = n s d p 2 [ s ] , ( i f s u m [ s ] 0 ) 0 , ( i f s u m [ s ] > 0 )

然后答案就等于
d p 1 [ s ] d p 2 [ t m p x o r s ] s u m [ s ]

大概好了。…
挺简单的呀?
为什么考场上想不出来呢?qwq

code

#include<bits/stdc++.h>
#define M 25
#define mo 998244353
using namespace std;
void read(int &x){
    x=0; char c=getchar(); int p=1;
    for (;c<48;c=getchar())if (c=='-')p=-1;
    for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
    x*=p;
}
#define lowbit(p) (p&(-p))
int a[M],sum[1<<20],dp[1<<20][2];
void add(int &x,int y){
    x=(x+y)%mo;
}
int main(){
//  freopen("1.in","r",stdin);
    int n,i,tmp,res=0,j;
    read(n);
    for (i=0;i<n;i++){
        read(a[i]);
        sum[1<<i]+=a[i];
        dp[1<<i][0]=1;
    }   
    tmp=1<<n;
    for (i=1;i<tmp;i++){
        sum[i]=sum[i^(lowbit(i))]+sum[lowbit(i)];
    }
    dp[0][0]=dp[0][1]=1;
    for (i=0;i<tmp;i++){
        for (j=0;j<n;j++)if (!(i&(1<<j))){
            if(sum[i]>0)add(dp[i|(1<<j)][0],dp[i][0]);
            if(sum[i|(1<<j)]<=0)add(dp[i|(1<<j)][1],dp[i][1]);
        }
    }
    for (i=1;i<tmp;i++){
        add(res,1ll*sum[i]*dp[i][0]%mo*dp[(tmp-1)^i][1]%mo);
    }
    printf("%d\n",(res+mo)%mo);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Umbrella__/article/details/80759396