1. 程式人生 > 實用技巧 >Luogu5369 [PKUSC2018]最大字首和

Luogu5369 [PKUSC2018]最大字首和

Description

link

給定一個排列\(\{a_{1\cdots n}\}\)

求其所有排列的最大字首和

\(n\le 20\)

Solution

最大字首和的本質是所有的補集字首和都小於 \(0\),然後這個排列的所有字尾和都是大於 \(0\)

注意是真字尾,所以正好處理掉了全是負數之類的情況

所以這題目可以考慮每個子集作為最大字首和的方案數再乘上它本身的和

(這兩步顯然)

列舉這個序列的所有子集作為最大字首和

然後是考慮該集合的補集有多少種排列方式使得所有字首和都小於 \(0\)

再考慮一個集合有多少種排列的方式使得該集合的所有真字尾和都不小於 \(0\)

可以做到 \(O(\log n)\)

求當前狀態下的集合求和

所有字首小於 \(0\) 好做

然後考慮剩下的部分:

這裡我想錯了,所以外加頹廢,卡了好久

考慮到我們說的是真字尾,不是字尾

所以定義:\(f_{S,0}\) 為集合元素的和小於 \(0\) 然後滿足真字尾都不小於 \(0\)

\(f_{S,1}\) 表示集合元素都大於等於 \(0\) 然後真字尾都不小於 \(0\) 的方案數

轉移?

\(f_{S,0}\)\(f_{S,1}\) 分別加上那些滿足條件的 \(f_{S',1}\)

最後還是一樣統計答案……

剩下一個取模比較坑了……

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k=='-') f=-1;
		while(isdigit(k)) res=res*10+k-'0',k=getchar();
		return res*f;
	}
	const int N=25,SZ=1<<20,mod=998244353;
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	inline int del(int x,int y){return x-y<0?x-y+mod:x-y;}
	int a[N],n,s[SZ],f1[SZ][2],f2[SZ];
	inline int lowbit(int x){return x&(-x);}
	inline int getsum(int x)
	{
		int sum=0;
		for(int i=1;i<=n;++i)
		{
			if(x&(1<<(i-1))) sum+=a[i],x^=(1<<(i-1));
			if(!x) break;
		}return sum;
	}
	inline int calc(int x){int cnt=0; for(;x;x-=lowbit(x)) ++cnt; return cnt;}
	signed main()
	{
		n=read(); for(int i=1;i<=n;++i) a[i]=read();
		int S=(1<<n)-1;  
		for(int i=0;i<=S;++i) s[i]=getsum(i);  
		for(int i=1;i<=n;++i) if(a[i]>=0) f1[1<<(i-1)][1]=1; else f1[1<<(i-1)][0]=1;
		for(int st=1;st<=S;++st)
		{
			for(int i=1;i<=n;++i)
			{
				if(st&(1<<(i-1))) continue;
				if(s[st]+a[i]>=0) f1[st|(1<<(i-1))][1]=add(f1[st][1],f1[st|(1<<(i-1))][1]); 
				else f1[st|(1<<(i-1))][0]=add(f1[st][1],f1[st|(1<<(i-1))][0]);
			}
		} 
		f2[0]=1;
		for(int st=0;st<=S;++st)
		{
			if(!f2[st]) continue; 
			for(int i=1;i<=n;++i) 
			{
				if(st&(1<<(i-1))) continue;
				if(s[st|(1<<(i-1))]<0) f2[st|(1<<(i-1))]=add(f2[st],f2[st|(1<<(i-1))]); 
			}
		} 
		int ans=0;
		for(int i=0;i<=S;++i) ans=add(ans,f2[S^i]*add(s[i],mod)%mod*(f1[i][0]+f1[i][1])%mod),ans+=mod,ans%=mod;
		printf("%lld\n",ans);
		return 0;
	}
}
signed main(){return yspm::main();}