cf1371E2.Asterism,数学,同余

题目链接

题意:

给一个数组 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>aix=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,pn,要求 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 mn+1xm1
因为当 x ≥ m x\geq m xm后, 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 iaix,那么对于排列 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,ajx+1,因为选择 P 0 P_0 P0已经用掉了一个数,所以选择 P 1 P_1 P1 j − 1 j-1 j1中方案,那么表达成公式就是 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+n1bi(ix),其中 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+n1bi(ix)=i=xx+n1x(ibi)
如果乘数 x − ( i − b i ) ≡ 0 ( m o d   p ) x-(i-b_i)\equiv 0(mod \ p) x(ibi)0(mod p),那么即 x ≡ i − b i ( m o d   p ) x\equiv i-b_i(mod\ p) xibi(mod p).
那么问题就转换成了预处理出所以的 i − b i i-b_i ibi,然后依次枚举 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(mn),这样每一个 a i ≤ n a_i\leq n ain,可以顺序统计出 b i b_i bi
此时 x x x的取值范围为 1 ≤ x < n − 1 1\leq x <n-1 1x<n1,那么 b i b_i bi中的 i i i最大可能到 2 ∗ n 2*n 2n
把所有的 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值