CF1612G Max Sum Array

Max Sum Array

Luogu CF1612G

题面描述

  • 给定一个长为 \(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';
}
posted @ 2024-01-20 15:51  Hanx16Msgr  阅读(5)  评论(0编辑  收藏  举报