Codeforces Round #654 E. Asterism
题目地址:
http://codeforces.com/contest/1371/problem/E2
E1基本思路:
对于E1我们考虑去枚举 x x x,每次独立判断 x x x是否符合 f ( x ) f(x) f(x)不被 p p p整除;
根据题目条件,我们容易知道在 x x x确定的情况下,Yuzu在每个位置 i i i 的糖果数就也确定了是 x + i − 1 x + i - 1 x+i−1;
那么我们要不败的话,当前位置敌人的糖果一定要小于Yuzu在这个位置的糖果数,那么我们进行一个二分就能找到可供放置的敌人的数量设为 p o s i pos_{i} posi,再减去之前已经确定的 ( i − 1 ) (i-1) (i−1)个敌人,我们就能知道每个位置还能供我们选择的敌人就有 p o s i − i + 1 pos_{i} - i + 1 posi−i+1个;
所以此时的全排列数量也就是 f ( x ) f(x) f(x)应该是 ∏ i = 1 n ( p o s i − i + 1 ) \prod_{i=1}^{n} (pos_{i} - i + 1) ∏i=1n(posi−i+1),要不被素因子 p p p整除,只要对于每个项判断是否能被整除就行了。
E1参考代码:
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define INF (int)1e18
inline int read() {
int x = 0, neg = 1; char op = getchar();
while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
return neg * x;
}
inline void print(int x) {
if (x < 0) { putchar('-'); x = -x; }
if (x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
const int maxn = 2010;
int n,p,a[maxn];
signed main() {
IO;
cin >> n >> p;
rep(i,1,n) cin >> a[i];
sort(a + 1 , a + 1 + n);
vector<int > ans;
for(int x = 1 ; x <= 2000 ; x++){
bool v = true;
for(int i = 1 ; i <= n ; i++){
int pos = upper_bound(a + 1, a + 1 + n,x + i - 1) - a - 1;
if((pos - i + 1) % p == 0) { v = false; break; }
}
if(v) ans.push_back(x);
}
cout << ans.size() << '\n';
for(auto it : ans) cout << it << ' ';
cout << '\n';
return 0;
}
E2基本思路:
对于E2肯定我们不能枚举 x x x,那么通过E1的代码我们容易发现答案都是连续的,所以我们可以思考 f ( x ) f(x) f(x)% p = = 0 p==0 p==0是否具有单调关系,如果有单调关系,那么很明显我们就能二分 x x x降低复杂度了。
那么我们如何证明 f ( x ) f(x) f(x)对 p p p取模一定的单调的,或者说证明对于某一 x i x_{i} xi出现了 f ( x i ) f(x_{i}) f(xi)能被 p p p整除,那么之后更大 x > x i x > x_{i} x>xi的 f ( x ) f(x) f(x)一定也会被 p p p整除。
对于 x x x的增大,我们发现对于每个位置的 p o s i − i + 1 pos_{i} - i + 1 posi−i+1一定的单调不减的,而且这部分是以一种阶乘的方式,从大的数往下依次递减,然后再回到一个大数的(打表可以发现,应该也可以证明,太菜了证明不来),所以就是说只要最大的 p o s i − i + 1 pos_{i} - i + 1 posi−i+1它大于等于 p p p,那么对于随着 x x x的增大,单调不减的最大 p o s i − i + 1 pos_{i} - i + 1 posi−i+1也会增大,而由于这部分具有类似阶乘的递减关系,就一定也会存在一个位置会被 p p p整除。
因此,二分是成立的,而最小的x也就是二分的左边界就应该是敌人按顺序排列的情况,能够直接算到这个最小x,然后我们二分找到最大x,这之间的就是所有符合条件的答案了。
E2参考代码:
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define INF (int)1e18
inline int read() {
int x = 0, neg = 1; char op = getchar();
while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
return neg * x;
}
inline void print(int x) {
if (x < 0) { putchar('-'); x = -x; }
if (x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
const int maxn = 1e5 + 10;
int n,p,a[maxn];
bool check(int x) {
for (int i = 1; i <= n; i++) {
int pos = upper_bound(a + 1, a + 1 + n, x + i - 1) - a - 1;
if ((pos - i + 1) % p == 0) return false;
}
return true;
}
signed main() {
IO;
cin >> n >> p;
rep(i, 1, n) cin >> a[i];
sort(a + 1, a + 1 + n);
int mn = 1;
for(int i = 1 ; i <= n ; i++) mn = max(mn,a[i] - i + 1);
int l = mn,r = INF,ans = -1;
while (l <= r) {
int mid = (l + r) / 2;
if (check(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
}
if(ans == -1){
cout << 0 << '\n';
return 0;
}
cout << ans - mn + 1 << '\n';
for(int i = mn ; i <= ans ; i++) cout << i << ' ';
cout << '\n';
return 0;
}