首页 > 其他分享 >P5369 [PKUSC2018]最大前缀和

P5369 [PKUSC2018]最大前缀和

时间:2022-11-01 21:12:25浏览次数:53  
标签:最大 前缀 sum 转移 P5369 PKUSC2018 define

题意

给定一个序列 \(a\),求 \(a\) 的所有排列的最大前缀和的和。

\(1\le n\le 20\)。

Solution

考虑到 \(n\) 很小的性质,想到状压。

先考虑一手,最大前缀和应该满足什么条件。一段前缀 \([1,i]\) 为最大前缀和,当且仅当,在 \([1,i]\) 的任意一段后缀都不为负,并且 \([i+1,n]\) 的任意一段前缀都是负的。

止步于此。实际上好好利用这个性质就可以轻松设计状态了。

\(\bigstar\) 我们把最后的答案拆成某一段最大前缀和,和除了这个前缀和外的后缀。那么前缀满足最大前缀和等于这个前缀中所有数的和,而后缀满足最大前缀和为负。

我们令 \(sum_S\) 表示集合 \(S\) 中的数的和,\(f_S\) 表示对集合 \(S\) 中的数进行排列后最大前缀和等于 \(sum_S\) 的方案数,\(g_S\) 表示对 \(S\) 中的数进行排列后最大前缀和为负的方案数。

考虑转移。先考虑 \(g\) 的转移,随便挑一个数放到最后,这样前面整段仍然需要满足最大前缀和为负,然后最后那个数需要满足它的权值加上前面整段的权值仍然为负即可。

然后考虑 \(f\)。考虑刷表。如果当前的和是负数,那么肯定不能通过在前面加某一个数来转移到更大的集合。所以能转移时和一定是正数。如果是正数,那么在前面加上任何数都可以转移。这样就可以通过刷表的方式转移 \(f\) 了。

最终答案应该是:

\[\sum_{S}sum_S\times f_S\times g_{\overline{S}} \]

Code

// Problem: 
//     P5369 [PKUSC2018]最大前缀和
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5369
// Memory Limit: 500 MB
// Time Limit: 2000 ms

#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
#define int long long
using namespace std;
const int MAXN=(1<<20)+10;
const int MOD=998244353;
int lbt(int x){return x&(-x);}
int a[22],f[MAXN],g[MAXN],sum[MAXN];
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n;cin>>n;
	rep(i,1,n) cin>>a[i];
	int lmt=(1<<n)-1;
	rep(i,1,lmt)
		sum[i]=sum[i-lbt(i)]+a[__lg(lbt(i))+1];
	g[0]=f[0]=1;
	rep(i,0,lmt){
		if(i!=0){
			rep(j,1,n) if((i>>(j-1))&1){
				if(a[j]+sum[i-(1<<(j-1))]<0)
				g[i]=(g[i]+g[i-(1<<(j-1))])%MOD;
			}
		}if(i!=lmt){
			if(sum[i]>=0)
			rep(j,1,n) if(!((i>>(j-1))&1)){
				(f[i+(1<<(j-1))]+=f[i])%=MOD;
			}
		}
	}
	int ans=0;
	rep(i,0,lmt) ans=(ans+sum[i]*f[i]%MOD*g[lmt^i]%MOD)%MOD;
	cout<<(ans%MOD+MOD)%MOD<<'\n';
	return 0;
}

标签:最大,前缀,sum,转移,P5369,PKUSC2018,define
From: https://www.cnblogs.com/ZCETHAN/p/16849165.html

相关文章

  • Nt、Ki、Zw等前缀含义
    搜索到如下回答。 https://stackoverflow.com/questions/4770553/windows-native-api-when-and-why-use-zw-vs-nt-prefixed-api-callsWindows本机系统服务例程的名称......
  • 洛谷 P5369
    先规定一些东西:若存在多个\(p\)使得\(\sum_{i=1}^{p}{a_i}\)最大,默认最大(即最靠右)的一个\(p\)是它的最大前缀和的位置。\(U\)表示全集。假设\(p\)是最大前缀......
  • 862. 和至少为 K 的最短子数组 : 前缀和 + 离散化 + 权值树状数组
    题目描述这是LeetCode上的​​863.二叉树中所有距离为K的结点​​,难度为困难。Tag:「前缀和」、「离散化」、「二分」、「树状数组」给你一个整数数组​​nums​......
  • 【P4198】楼房重建(线段树维护前缀最值)
    线段树维护前缀最值相关的模板题。关键思想在于合并,\([l,mid]\)的前缀最值仍然是\([l,r]\)的前缀最值,而\((mid,r]\)中只有\(\geqmx_l\)的前缀最值才是\([l,r]\)......
  • 最长公共前缀
    题目:求字符串数组中元素的最长公共前缀,如输入["flower","flow","flight"]输出"fl"。解题思路:1.遍历其中任意一个元素的所有字符,并遍历所有元素,比较当前字符串是否与......
  • leetcode(32)前缀和系列题目
    303.区域和检索-数组不可变记录前i个元素的和,因此sum[left,right+1]=pre[right+1]-pre[left]classNumArray:def__init__(self,nums:List[int]):......
  • 2017蓝桥杯 K倍区间 前缀和+同余定理
    2017蓝桥杯K倍区间前缀和+同余定理给定一个长度为的数列,。如果其中一段连续的子序列之和是的倍数,我们就称这个区间是倍区间。你能求出数列中总共有多少个倍区间吗?看到“......
  • leetcode 14. 最长公共前缀 (绝对想不到的解法)
    题目在这:https://leetcode-cn.com/problems/longest-common-prefix/先说一下传统的解法,毕竟刷leecode是为了提升算法能力,直接用函数不太可取哈哈.题目要找最长公共前缀,是......
  • 【前缀和】560. 和为 K 的子数组
    给你一个整数数组nums和一个整数 k,请你统计并返回该数组中和为 k 的连续子数组的个数 。 示例1:输入:nums=[1,1,1],k=2输出:2示例2:输入:nums=[1,2,3],k......
  • 【luogu ARC106E】Medals(二分)(高维前缀和)
    Medals题目链接:luoguARC106E题目大意有n个第i个人的出现规律是对于所有2aik+1~2ai(k+1)的区间,2aik+1~2aik+ai会出现,另一部分则会不见。每个时间点你可以选择一......