zoukankan      html  css  js  c++  java
  • 【洛谷P5369】最大前缀和

    题目

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

    思路

    (sum[s]) 表示集合 (s) 中所有元素之和。设 (f[s]) 表示集合为 (s) 时,最大前缀和恰好为 (sum[s]) 的方案数。(g[s]) 表示集合为 (s) 时,最大前缀和为 (0) 的方案数。
    那么答案显然就是

    [sum^{2^n-1}_{i=1}sum[i] imes f[i] imes g[2^n-1-i] ]

    接下来考虑转移。
    如果 (sum[s]geq 0),那么我们可以通过在集合 (s) 前加入一个数字 (i) 来转移到 (f[s m xor i])。否则无法对 (f[s m xor i]) 产生贡献。
    如果 (sum[s]<0),那么对于一个在 (s) 里的数 (i)(g[s]) 可以通过 (g[s m xor i]) 转移过来。
    时间复杂度 (O(2^nn))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=25,M=(1<<20),MOD=998244353;
    int n,ans,lim,a[N],f[M],g[M],sum[M];
    
    int main()
    {
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    		scanf("%d",&a[i]),f[1<<i-1]=1;
    	g[0]=1; lim=1<<n;
    	for (int s=1;s<lim;s++)
    	{
    		for (int i=1;i<=n;i++)
    			if (s&(1<<i-1)) sum[s]+=a[i];
    		if (sum[s]<0)
    			for (int i=1;i<=n;i++)
    				if (s&(1<<i-1)) g[s]=(g[s]+g[s^(1<<i-1)])%MOD;
    		if (sum[s]>=0)
    			for (int i=1;i<=n;i++)
    				if (!(s&(1<<i-1))) f[s|(1<<i-1)]=(f[s|(1<<i-1)]+f[s])%MOD;
    	}
    	for (int i=0;i<lim;i++)
    		ans=(ans+1LL*sum[i]%MOD*f[i]%MOD*g[(lim-1)^i])%MOD;
    	cout<<(ans%MOD+MOD)%MOD;
    	return 0;
    }
    
  • 相关阅读:
    Flutter环境配置(window10环境)最完善版本
    ElementUI el-date-picker 限制选中日期前后30天,大于当天不可选
    fastreport添加每页的小计
    c# 类似sql中的isnull()语法
    HttPost HttpGet
    LaTeX幻灯片主题和颜色预览
    记录
    交易思想之顺大势逆小势
    Python读写json文件
    Python实现QQ PC端给好友发送消息
  • 原文地址:https://www.cnblogs.com/stoorz/p/15017090.html
Copyright © 2011-2022 走看看