zoukankan      html  css  js  c++  java
  • bzoj 5369: [Pkusc2018]最大前缀和

    Description

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

    Solution

    注意到前缀和的取值只有 (2^n) 种.
    然后可以枚举每一个集合的元素当最大前缀和 , 那么这个集合的元素排列之后每一个后缀都必须大于 (0) , 且这个集合的补集排列之后必须保证每一个前缀和都小于 (0).
    那么状压 (DP) 就行了 , 设 (f[i]) 表示集合 (i) 作为最大前缀和且排列之后每个后缀都大于 (0) 的方案数 , (g[i]) 表示集合 (i) 中元素排列之后每个前缀都小于 (0) 的方案数.
    强制 (f,g) 必须在合法的时候才能转移就行了.

    #include<bits/stdc++.h>
    using namespace std;
    const int N=25,mod=998244353;
    int f[1<<20],n,a[N],m,g[1<<20],sum[1<<20];
    int main(){
      freopen("pp.in","r",stdin);
      freopen("pp.out","w",stdout);
      cin>>n,m=(1<<n)-1;
      for(int i=0;i<n;i++)cin>>a[i];
      for(int i=0;i<n;i++)
    	  for(int j=0;j<=m;j++)if(j>>i&1)sum[j]+=a[i];
      for(int i=0;i<n;i++)f[1<<i]=1,g[1<<i]=1;
      for(int i=0;i<=m;i++){
    	  if(sum[i]>0){
    		  for(int j=0;j<n;j++)
    			  if(~i>>j&1)f[i^(1<<j)]=(f[i^(1<<j)]+f[i])%mod;
    	  }
    	  else{
    		  for(int j=0;j<n;j++)
    			  if(~i>>j&1)g[i^(1<<j)]=(g[i^(1<<j)]+g[i])%mod;
    	  }
      }
      g[0]=1;
      int ans=0;
      for(int i=0;i<=m;i++)
    	  if(sum[m^i]<=0)ans=(ans+1ll*f[i]*sum[i]%mod*g[m^i])%mod;
      cout<<(ans+mod)%mod;
      return 0;
    }
    
    
  • 相关阅读:
    排序之选择排序
    排序之冒泡排序
    NOIP 模拟 $22; m d$
    NOIP 模拟 $20; m z$
    NOIP 模拟 $20; m y$
    NOIP 模拟 $20; m 玩具$
    NOIP 模拟 $21; m Median$
    NOIP 模拟 $21; m Park$
    NOIP 模拟 $21; m Game$
    NOIP 模拟 $19; m w$
  • 原文地址:https://www.cnblogs.com/Yuzao/p/9304011.html
Copyright © 2011-2022 走看看