CodeForces 1371E1&E2 Asterism (Easy Version & Hard Version) (暴力 | 二分)

题意:有 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<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#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<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值