[题目链接]
https://codeforces.com/contest/1188/problem/E
[题解]
记 (x_{i}) 表示 (i) 被选中的次数 , 考虑 ({x_i}) 合法的条件 , 令 (s = sum{x_{i}})。
首先需要保证最终数组中的每个元素非负 , 故 (a_{i} - s + k cdot x_{i} geq 0) , 也就是 (x_igelceilfrac{max(s-a_i,0)}k ceil)。
此外 , 还需判断每轮后是否能使得每个元素都非负 , 假设当前是第 (t) 轮 , 不妨将每个 (a_{i}) 都减去 (t) , 再用 (t) 个 (k) 填补空缺 , 需要满足 (sum_{i=1}^klceilfrac{max(t-a_i,0)}k ceille t) , 在第一个条件的限制下 , 每个元素不会被加超过 (x_{i}) 次。
故一个 ({x_i}) 满足条件当且仅当 :
(1). (x_igelceilfrac{max(s-a_i,0)}k ceil)
(2). 对于 (0le tle s) , 都有 (sum_{i=1}^klceilfrac{max(t-a_i,0)}k ceille t)。
计算 (sum_{i=1}^klceilfrac{max(t-a_i,0)}k ceille t) 是容易的 , 只需对于 (a_{i} < s) 的部分开一个桶维护 (a_{i} mod k) 即可。
考虑当所有 (x_{i}) 非负时 , 将其全部减一本质上是同一种情况。
结论 :所有至少有一个数未操作的不同的 ({x_i}) , 两两本质不同
证明 :
((1).) 若操作集合相同 , 若操作次数不同 , 因为有数没被操作 , 故最终得到的序列必然本质不同; 若操作次数相同 , 则必能找到两个数使其得到的结果不同。
((2).) 若操作集合不同 , 设 (i) 在第一个序列未被操作但在第二个序列中被操作 , (j) 在第一个序列被操作但在第二个序列中未被操作。 若操作次数相同 , 那么有 (delta a_{i , 1} = -cnt , delta a_{i , 2} = C cdot k - cnt eq delta a_{i , 1})。若操作次数不同 , (delta a_{i , 1} = -cnt_{1} , delta a_{j , 2} = -cnt_{2})。 因为 (a_{j , 1} = C dot k - cnt_{1} = -cnt_{2}) , 所以 (cnt_{1} >cnt_{2}) , 同理又有 (cnt_{2} > cnt_{1}) , 矛盾。
故我们成功地将问题转化为对 ({x_i}) 计数。不难发现新问题中 (s) 的规模被缩小至 (max{a_{i}})
也就是说 , 问题变为了 :
有 (k) 个变量 , 其中有 (r) 个 ((a_{i} < s) 的个数) 有一个取值下界 (lim_{i}) 且 (lim_{i} geq 1) , 求这 (k) 个变量中至少有一个为 (0) 且总和为 (s) 的方案数。
不妨令 (w = sum{lim_{i}}) 。
考虑容斥原理 , 用总方案数减去至少有一个 (0) 的方案数 , 这两者都可以用组合数学中经典的 "插板法" 求解。
答案为 (inom{s-w+k-1}{k-1}-inom{s-w+r-1}{k-1})。
故时间复杂度 (O(max{a_{i}} + KlogK))
[代码]
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i , l , r) for (int i = (l); i < (r); ++i)
const int MN = 2e6 + 5 , mod = 998244353;
int K , N , A[MN] , fac[MN] , ifac[MN] , cnt[MN];
inline void inc(int &x , int y) {
x = x + y < mod ? x + y : x + y - mod;
}
inline void dec(int &x , int y) {
x = x - y >= 0 ? x - y : x - y + mod;
}
inline int qPow(int a , int b) {
int c = 1;
for (; b; b >>= 1 , a = 1ll * a * a % mod) if (b & 1) c = 1ll * c * a % mod;
return c;
}
inline void init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; ++i) fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[n] = qPow(fac[n] , mod - 2);
for (int i = n - 1; i >= 0; --i) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
}
inline int binom(int n , int m) {
if (n < m) return 0;
else return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
int main() {
init(2e6);
scanf("%d" , &K); int cur = 0;
for (int i = 1; i <= K; ++i) scanf("%d" , &A[i]) , N += A[i];
sort(A + 1 , A + 1 + K); int ans = 0;
for (int i = 0 , j = 1; i <= A[K]; ++i) {
while (A[j] < i) ++cnt[A[j++] % K];
cur += cnt[(i - 1 + K) % K];
if (cur > i) {
printf("%d
" , ans);
return 0;
}
inc(ans , binom(i - cur + K - 1 , K - 1));
if (i - cur + j - 2 >= K - 1)
dec(ans , binom(i - cur + j - 2 , K - 1));
}
printf("%d
" , ans);
return 0;
}