题意:有 n 个敌人,编号为 1∼n,第 i 个敌人有 ai 个糖果。Yuzu在最开始时有 x 个糖果。当Yuzu拥有的糖果数大于等于她此时面对的敌人的糖果数时,它可以击败这个敌人,并取得1个糖果,否则她将被敌人击败,并且什么也得不到。Yuzu希望对所有的敌人都取得胜利,请帮她重新安排 n 个敌人的出现顺序,即 1∼n 的一个合法的排列 P。让我们定义 f(x) 等于初始时Yuzu有 x 个糖果时这样的排列 P 的数量。给出 n,p,其中 p 是质数,并且 p≤n。 我们称 x 是好的,当且仅当 f(x) 不能被 p 整除。找到所有好的 x。
此题分为 Easy 和 Hard 两个版本。
Easy 版本 2≤p≤n≤2000,1≤ai≤2000。
Hard 版本 2≤p≤n≤1e5,1≤ai≤1e9。
题解:暴力 | 二分
由于要将所有敌人击败,那么在面对最后一个敌人的时候,拥有的糖果数量就是x+n-1。
若x+n-1 < max(a1,a2,…,an),则f(x) = 0,能被整除;
若初始糖果数量 x ≥ max(a1,a2,…,an),f(x) = n!,由于p ≤ n,能被整除。
所以我们只需要考虑 max(a1,a2,…,an) - n + 1 ≤ x < max(a1,a2,…,an)。
接下来我们计算f(x),考虑两种情况:
先明确一点,敌人糖果数量必须 ≤ x + n - 1。
①当前敌人糖果数量 > x,那么他所能排的位置数量就是(x + n - 1) - a[j] + 1,同时要减去已经排掉的位置。
②当前敌人糖果数量 ≤ x,那么他可以任意排。
然后不断累乘即可,看最终答案是否能被整除。
时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
Easy Version:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
int n, p, a[2005];
vector<int> v;
int main() {
scanf("%d%d", &n, &p);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
for (int i = max(1, a[n] - n + 1); i < a[n]; i++) { //x的值
int minx = i;
int maxx = i + n - 1;
int flag = 1;
int fx = 1;
for (int j = n, num = 0; j >= 1; j--, num++) {
if (fx == 0) break;
if (a[j] > minx) {
if (maxx - a[j] + 1 - num <= 0) fx = 0;
else fx = (fx * (maxx - a[j] + 1 - num)) % p;
}
else {
if (n - num <= 0) fx = 0;
else fx = (fx * (n - num)) % p;
}
}
if (fx) v.push_back(i);
}
int sz = v.size();
printf("%d\n", sz);
for (int i = 0; i < sz; i++) printf("%d ", v[i]);
return 0;
}
实在想不到可以这样做,二分很好理解,主要是将方案数转换成同余关系,然后用 S u f Suf Suf数组记录下标。
Hard Version题解来源
Hard Version:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
int n, p, a[100005], b[100005];
vector<int> v;
int main() {
scanf("%d%d", &n, &p);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
int temp = (a[n - i + 1] + i - 1) % p;
b[temp] = max(b[temp], n - i + 1);
}
for (int i = max(1, a[n] - n + 1); i < a[n]; i++) { //x的值
int minx = i;
int maxx = i + n - 1;
int pos = upper_bound(a + 1, a + n + 1, minx) - a - 1; //可能不是合法序列,找到最后一个等于minx的数下标
if (pos >= p) continue;
if (b[(i + n) % p] > pos) continue;
v.push_back(i);
}
int sz = v.size();
printf("%d\n", sz);
for (int i = 0; i < sz; i++) printf("%d ", v[i]);
return 0;
}