[CF1188E] Problem from Red Panda

[题目链接]

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;
}
原文地址:https://www.cnblogs.com/evenbao/p/14354437.html