CF1612G Max Sum Array
Max Sum Array
题面描述
- 给定一个长为 \(m\) 的序列 \(c_1,c_2,\dots,c_m\)。
- 序列 \(A\) 满足:对于所有 \(1 \leq i \leq m\),\(i\) 在 \(A\) 中出现了 \(c_i\) 次。
- 定义一个序列 \(A\) 的值如下:
\[f(A)=\sum_{1 \leq i<j \leq n,a_i=a_j}j-i
\]
- 求满足条件的 \(f(A)\) 的最大值,及在取最大值时有多少种序列 \(A\)。
- \(1 \leq m \leq 5 \times 10^5,1 \leq c_i \leq 10^6\)。
Solution
分开讨论每一种颜色。
假设一个颜色出现的位置为 \(p_1,p_2,p_3,\dots,p_n\)。拆一下贡献的式子,发现对于一个位置 \(p_i\),它最后会对答案造成 \((2i-n-1)p_i\) 的贡献。由于每个颜色出现的次数固定,因此所有的系数也是固定的,所以只需要找到一种 \(p\) 的分配方案使得答案最小即可。不难发现 \(p\) 只需要按照系数的大小关系分配,系数大的 \(p\) 也分配大的位置即可。
对于方案的求法,当两个位置的系数相等的时候,这两个位置一定可以交换且不影响答案。所以记 \(\operatorname{cnt}_i\) 表示系数为 \(i\) 的位置个数,那么最后的方案数应当为 \(\displaystyle\sum\operatorname{cnt}_i!\)。
如果直接按照上面的做法做,时间复杂度是 \(\mathcal O(m^2)\) 的。发现每一个颜色对系数桶的贡献其实相当于是一个步长为 \(2\) 的区间加,那么直接使用差分快速维护系数桶即可。
时间复杂度 \(\mathcal O(m)\)。
Code
int N, cnt[_N];
mint fac[_N];
void init(int n) {
fac[0] = 1; For(i, 1, n) fac[i] = fac[i-1] * i;
}
signed main() {
cin.tie(0)->sync_with_stdio(0);
cin >> N; init(M);
For(i, 1, N) {
int x; cin >> x;
++cnt[M-x], --cnt[M+x];
}
i64 sum = 0;
mint ans = 0, tot = 1;
auto calc = [](i64 l, i64 r)->mint {
return (l + r) * (r - l + 1) / 2 % mod;
};
For(i, 2, M << 1) {
cnt[i] += cnt[i-2];
ans += calc(sum + 1, sum + cnt[i]) * (mint(i) - M + 1);
tot *= fac[cnt[i]];
sum += cnt[i];
}
cout << ans << ' ' << tot << '\n';
}