洛谷 - P5369

唐题。

分析

考虑一个数列 \([1,i]\) 前缀最大的充要条件,当且仅当:

  • \(\forall k \in [1,i]\),满足 \(\sum\limits_{j=k}^i a_j \ge 0\)
  • \(\forall k \in (i,n]\),满足 \(\sum\limits_{j=i+1}^j a_j < 0\)

考虑对满足第一/二种条件的排列进行计数。
\(f_{S}\) 为当前被使用的状态为 \(S\),从前往后填到 \(popcount(S)\) 位的方案数,转移有:

\[f_{S} \rightarrow f_{S+T},T\notin S,sum_S \ge 0 \]

第二种条件同理。

代码

n=rd();
	for(int i=0;i<n;i++)
		a[i]=rd(),f[1<<i]=1;
	int all=(1<<n)-1;
	for(int S=1;S<=all;S++)
		for(int i=0;i<n;i++)
			if(S>>i&1)
				sum[S]+=a[i];
	g[0]=1;
	for(int S=1;S<=all;S++){
		if(sum[S]>=0){
			for(int i=0;i<n;i++)
				if(!(S>>i&1))
					f[S|(1<<i)]=(f[S|(1<<i)]+f[S])%mod;	
		}
		else{
			for(int i=0;i<n;i++)
				if(S>>i&1)
					g[S]=(g[S]+g[S^(1<<i)])%mod;	
		}
	}
	int ans=0;
	for(int S=1;S<=all;S++){
		ans=(ans+f[S]*g[all^S]%mod*sum[S]%mod+mod)%mod;
	}
	cout<<ans<<endl;

posted @ 2024-08-02 07:52  SmileMask  阅读(6)  评论(0编辑  收藏  举报