• BZOJ5369:[PKUSC2018]最大前缀和(状压DP)


    Description

    小C是一个算法竞赛爱好者,有一天小C遇到了一个非常难的问题:求一个序列的最大子段和。
    但是小C并不会做这个题,于是小C决定把序列随机打乱,然后取序列的最大前缀和作为答案。
    小C是一个非常有自知之明的人,他知道自己的算法完全不对,所以并不关心正确率,他只关心求出的解的期望值,
    现在请你帮他解决这个问题,由于答案可能非常复杂,所以你只需要输出答案乘上n!后对998244353取模的值,显然这是个整数。
    注:最大前缀和的定义:i∈[1,n],Sigma(aj)的最大值,其中1<=j<=i

    Input

    第一行一个正整数nnn,表示序列长度。
    第二行n个数,表示原序列a[1..n],第i个数表示a[i]。
    1≤n≤20,Sigma(|Ai|)<=10^9,其中1<=i<=N

    Output

    输出一个非负整数,表示答案。

    Sample Input

    2
    -1 2

    Sample Output

    3

    Solution

    首先对于一个序列$[a_1,a_n]$,设最大前缀和的位置为$p$,那么序列$[a_{p+1},a_n]$的任意一个前缀必须都$<=0$。否则的话你用最大前缀和随便加上$[a_{p+1},a_n]$中$>0$的一个前缀就可以得到新的最大前缀和。

    预处理:

    $sum[S]$表示集合$S$的数字和。

    $f[S]$表示钦定集合$S$当最大前缀的合法方案数。

    $g[S]$表示集合$S$任意前缀和$<=0$小于$0$的方案数。

    那么显然$ans=sum sum[S] imes f[S] imes g[S']$。其中$S'$是$S$的补集。

    $sum$和$g$都是可以直接求的,那么$f$呢?

    可以发现,如果$sum[S]>0$,那么把随便一个数放到这个集合$S$的最前面,这个最大前缀和仍然是可以保证合法的。

    $ans$最后忘了取模$WA$了好几发……心态崩了

    Code

     1 #include<iostream>
     2 #include<cstdio>
     3 #define N (21)
     4 #define MOD (998244353)
     5 using namespace std;
     6 
     7 int n,m,a[N],sum[1<<N],cnt[1<<N],f[1<<N],g[1<<N];
     8 
     9 int main()
    10 {
    11     scanf("%d",&n); m=(1<<n)-1;
    12     for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
    13     for (int i=1; i<=n; ++i)
    14         for (int S=0; S<=m; ++S)
    15             if (S&(1<<i-1)) sum[S]+=a[i], cnt[S]++;
    16             
    17     for (int S=1; S<=m; ++S)
    18     {
    19         if (cnt[S]==1) {f[S]=1; continue;}
    20         for (int i=1; i<=n; ++i)
    21             if ((S&(1<<i-1)) && sum[S]-a[i]>0)
    22                 (f[S]+=f[S^(1<<i-1)])%=MOD;
    23     }
    24     
    25     g[0]=1;
    26     for (int S=1; S<=m; ++S)
    27     {
    28         if (sum[S]>0) {g[S]=0; continue;}
    29         if (cnt[S]==1) {g[S]=1; continue;}
    30         for (int i=1; i<=n; ++i)
    31             if (S&(1<<i-1))
    32                 (g[S]+=g[S^(1<<i-1)])%=MOD;
    33     }
    34     int ans=0;
    35     for (int S=1; S<=m; ++S)
    36         (ans+=1ll*sum[S]*f[S]%MOD*g[m^S]%MOD)%=MOD;
    37     ans=(ans%MOD+MOD)%MOD;
    38     printf("%d
    ",ans);
    39 }
  • 相关阅读:
    [loj2706]文本编辑器
    [atAGC053C]Random Card Game
    [atAGC056E]Cheese
    [cf1615G]Maximum Adjacent Pairs
    [cf739D]Recover a functional graph
    [uoj693]地铁规划
    [atAGC053E]More Peaks More Fun
    [atAGC056B]Range Argmax
    [atAGC056F]Degree Sequence in DFS Order
    SceneGrabber NET 视频批量自动截图软件使用技巧
  • 原文地址:https://www.cnblogs.com/refun/p/10158730.html
Copyright © 2020-2023  润新知