洛谷 - P5369

SmileMask發表於2024-08-02

唐題。

分析

考慮一個數列 \([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;