CF1612G Max Sum Array 做题记录

本题可以积累一个较常见的套路。

对于每一个 \(c_i\),考虑这 \(c_i\) 个数产生的贡献。如果设其在 \(a\) 中的下标为 \(x_j(j=1,2,...,c_i;x_j<x_{j+1})\),则它们产生总贡献是

\[\sum_{1\le j<k\le c_i}(x_k-x_j) \]

化简得

\[\sum_{j=1}^{c_i}(2j-c_i-1)x_j \]

单个元素产生的贡献是 \((2j-c_i-1)x_j\),可以把括号中的看成权值。

由此,这所有 \(\sum c_i\) 个数的贡献从大到小排序,依次给予下标 \(n,n-1,...,1\),既可以实现“大乘大”的贪心策略。
同时,对于第二问(方案数),权值相同的 \(l\) 个数可以全排列,产生 \(\times l!\) 的方案。

但这样的复杂度为 \(O(\sum c_i)\) 的。
考虑到实际有用的是权值的大小,可以建一个权值桶。对于每个 \(c_i\),第 \(1-c_i,3-c_i,...,c_i-3,c_i-1\) 个桶都需要加 1。
从大到小枚举权值,照旧给桶里的数分配下标。

对于实现方面,统一加可以用差分(稍加改动)。
\(O(n)\)
代码细节:权值会有负数,但绝对值不超过 \(10^6\),可以先统一加上 \(10^6\) 便于数组标记。

#include <bits/stdc++.h>
using namespace std;
const int N=2e6+5,mod=1e9+7;
typedef long long ll;
ll n,m,val,ans=1,c[N],b[2][N],jc[N];
signed main(){
	cin>>m;
	for(int i=1;i<=m;i++)cin>>c[i],n+=c[i];
	for(int i=1;i<=m;i++){
		ll x=1e6+1-c[i],y=1e6+c[i]-1;
		b[x&1][x]++,b[x&1][y+1]--;
	}
	jc[0]=1;
	for(int i=1;i<=2e6;i++)b[0][i]+=b[0][i-1],b[1][i]+=b[1][i-1],jc[i]=jc[i-1]*i%mod;
	for(int i=2e6;i;i--){
		val=(val+(n+n+1-b[i&1][i])*b[i&1][i]/2%mod*((i-1000000)%mod+mod)%mod)%mod;
		ans=ans*jc[b[i&1][i]]%mod;
		n-=b[i&1][i];
	}
	cout<<val<<' '<<ans;
}

文首所指的套路指的是从第一公式到第二公式的化简。

posted @ 2022-01-07 13:57  pengyule  阅读(48)  评论(0编辑  收藏  举报