题意:
给一个数组
a
a
a,进行如下操作:
初始有一个值
x
x
x,对于
a
a
a的任意一个排列
P
P
P,依次比较
a
1
,
a
2
,
.
.
.
a
n
a_1,a_2,...a_n
a1,a2,...an,如果
x
>
a
i
,
则
x
=
x
+
1
x>a_i,则x=x+1
x>ai,则x=x+1,要求
x
x
x大于所有的
a
i
a_i
ai。
令
f
(
x
)
f(x)
f(x)为满足上述条件的
a
a
a的排列的个数。另外再给一个质数
p
,
p
≤
n
p,p \leq n
p,p≤n,要求
f
(
x
)
%
p
≠
0
f(x)\% p\neq 0
f(x)%p=0,找到所有满足条件的
x
x
x。
题解:
首先发现
x
x
x的值至少为
m
a
x
(
a
i
)
−
n
+
1
max(a_i)-n+1
max(ai)−n+1,最大值为
m
a
x
(
a
i
)
−
1
max(a_i)-1
max(ai)−1,令
m
=
m
a
x
(
a
i
)
m=max(a_i)
m=max(ai),则
m
−
n
+
1
≤
x
≤
m
−
1
m-n+1\leq x \leq m-1
m−n+1≤x≤m−1。
因为当
x
≥
m
x\geq m
x≥m后,
f
(
x
)
=
n
!
f(x)=n!
f(x)=n!,肯定被
q
q
q整除。
所以对
x
x
x的值进行一一遍历,然后求解即可。
那么问题就变成了对于
x
x
x如何求
f
(
x
)
f(x)
f(x)。
首先对
a
a
a数组排序,那么对于给定的
x
x
x,我们首先找到最大的
i
,
a
i
≤
x
i,a_i \leq x
i,ai≤x,那么对于排列
P
P
P的第一个位置我们可以选择
P
0
=
a
0
o
r
a
1
o
r
.
.
.
o
r
a
i
P_0=a_0 \ or\ a_1\ or \ ...\ or\ a_i
P0=a0 or a1 or ... or ai,也就是有
i
+
1
i+1
i+1种方案选择
P
0
P_0
P0位置上的数,对于
P
1
P_1
P1的选择,也是一样的,找到最大的
j
,
a
j
≤
x
+
1
j,a_j\leq x+1
j,aj≤x+1,因为选择
P
0
P_0
P0已经用掉了一个数,所以选择
P
1
P_1
P1有
j
−
1
j-1
j−1中方案,那么表达成公式就是
f
(
x
)
=
∏
i
=
x
x
+
n
−
1
b
i
−
(
i
−
x
)
f(x)=\prod_{i=x}^{x+n-1}b_i-(i-x)
f(x)=∏i=xx+n−1bi−(i−x),其中
b
i
b_i
bi是
a
a
a数组中小于等于
i
i
i的数的个数。判断
f
(
x
)
f(x)
f(x)是否满足条件只需要检查
f
(
x
)
f(x)
f(x)中每一个乘数是否包含素因子
p
p
p就可以了。
那么数据量较小的时候,直接暴力求解即可:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 10;
int main() {
int n, p;
cin >> n >> p;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a.begin(), a.end());
int Max = a[n-1];
set<int> res;
for (int i = Max-n+1; i < Max; i++) {
int x = i, v = 0, j = 0, k = 0;
bool flag = false;
while (k < n) {
while (j < n && a[j] <= x) {
v++; j++;
}
if (v % p == 0) {
flag = true;
break;
}
x++;
k++;
v = v-1;
}
if (!flag) {
res.insert(i);
}
}
cout << res.size() << endl;
for (int x : res) {
cout << x << " ";
}
cout << endl;
}
对于数据量较大的情况,需要做一点变形:
f
(
x
)
=
∏
i
=
x
x
+
n
−
1
b
i
−
(
i
−
x
)
=
∏
i
=
x
x
+
n
−
1
x
−
(
i
−
b
i
)
f(x)=\prod_{i=x}^{x+n-1}b_i-(i-x)=\prod_{i=x}^{x+n-1}x-(i-b_i)
f(x)=∏i=xx+n−1bi−(i−x)=∏i=xx+n−1x−(i−bi)
如果乘数
x
−
(
i
−
b
i
)
≡
0
(
m
o
d
p
)
x-(i-b_i)\equiv 0(mod \ p)
x−(i−bi)≡0(mod p),那么即
x
≡
i
−
b
i
(
m
o
d
p
)
x\equiv i-b_i(mod\ p)
x≡i−bi(mod p).
那么问题就转换成了预处理出所以的
i
−
b
i
i-b_i
i−bi,然后依次枚举
x
x
x,判断是否有因数与
x
%
p
x\%p
x%p同余。
由于
a
i
a_i
ai太大,所以首先对每个
a
i
=
a
i
−
(
m
−
n
)
a_i = a_i-(m-n)
ai=ai−(m−n),这样每一个
a
i
≤
n
a_i\leq n
ai≤n,可以顺序统计出
b
i
b_i
bi。
此时
x
x
x的取值范围为
1
≤
x
<
n
−
1
1\leq x <n-1
1≤x<n−1,那么
b
i
b_i
bi中的
i
i
i最大可能到
2
∗
n
2*n
2∗n。
把所有的
b
i
b_i
bi都统计出来之后,再用一个数组统计一下余数,然后顺序遍历就可以了。
#include <bits/stdc++.h>
using namespace std;
int mod(int x, int p) {
return (x%p + p) % p;
}
int main() {
int n, p;
cin >> n >> p;
vector<int> a(n), b(2*n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int Max = *max_element(a.begin(), a.end());
for (int i = 0; i < n; i++) {
b[max(0, a[i]-(Max-n))]++;
}
for (int i = 1; i < 2*n; i++) {
b[i] += b[i-1];
}
vector<int> f(n);
int shift = Max-n;
for (int i = 1; i <= n; i++) {
// x = Max-n+1,第一个x的值
// 统计每一个乘数的余数
// i在之前统一被减小了(Max-n),这里要加回来
f[mod(i+shift-b[i], p)]++; // (i-b[i])
}
vector<int> res;
for (int i = 1; i < n; i++) {
// 遍历每一个可能的x
// x = Max-n+i
if (f[mod(i+shift, p)] == 0) { // x
// 当前没有与x同余的乘数
res.push_back(i+shift);
}
// 去掉之前的一个因数
f[mod(i+shift-b[i], p)]--;
// 加上下一个因数
f[mod(n+i+shift-b[n+i], p)]++;
}
cout << res.size() << endl;
for (int x : res) {
cout << x << " ";
}
cout << endl;
}