Codeforces Round #654 E. Asterism (思维+二分)

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+i1;

那么我们要不败的话,当前位置敌人的糖果一定要小于Yuzu在这个位置的糖果数,那么我们进行一个二分就能找到可供放置的敌人的数量设为 p o s i pos_{i} posi,再减去之前已经确定的 ( i − 1 ) (i-1) (i1)个敌人,我们就能知道每个位置还能供我们选择的敌人就有 p o s i − i + 1 pos_{i} - i + 1 posii+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(posii+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 posii+1一定的单调不减的,而且这部分是以一种阶乘的方式,从大的数往下依次递减,然后再回到一个大数的(打表可以发现,应该也可以证明,太菜了证明不来),所以就是说只要最大的 p o s i − i + 1 pos_{i} - i + 1 posii+1它大于等于 p p p,那么对于随着 x x x的增大,单调不减的最大 p o s i − i + 1 pos_{i} - i + 1 posii+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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值