[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_i\ge\lceil\frac{\max(s-a_i,0)}k\rceil\)。

此外 , 还需判断每轮后是否能使得每个元素都非负 , 假设当前是第 \(t\) 轮 , 不妨将每个 \(a_{i}\) 都减去 \(t\) , 再用 \(t\) 个 \(k\) 填补空缺 , 需要满足 \(\sum_{i=1}^k\lceil\frac{\max(t-a_i,0)}k\rceil\le t\) , 在第一个条件的限制下 , 每个元素不会被加超过 \(x_{i}\) 次。

故一个 \(\{x_i\}\) 满足条件当且仅当 :

(1). \(x_i\ge\lceil\frac{\max(s-a_i,0)}k\rceil\)

(2). 对于 \(0\le t\le s\) , 都有 \(\sum_{i=1}^k\lceil\frac{\max(t-a_i,0)}k\rceil\le t\)。

计算 \(\sum_{i=1}^k\lceil\frac{\max(t-a_i,0)}k\rceil\le 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 \neq \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\) 的方案数 , 这两者都可以用组合数学中经典的 "插板法" 求解。

答案为 \(\binom{s-w+k-1}{k-1}-\binom{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\n" , 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\n" , ans);
     return 0;
}
上一篇:算法作业题解


下一篇:GCC的内联汇编