Educational Codeforces Round 156 (Rated for Div. 2)

写在前面

比赛地址:https://codeforces.com/contest/1886

呃呃幸亏 C 还调出来了,要不然真身败名裂了。

赛后看大神题解发现 D 是傻逼题,赛时还剩十分钟快睡着了,难受了这下。

再来一首铃果。

声线好听唱功还了得,太喜欢了呜呜

话说铃果参加超时空要塞\(\Delta\)甄选会选的时候才 17 啊我草,大神、、、

A

发现 \(\le 6\) 都不行,9 也不行,除此之外都可以凑成 \(1, 2, n-3\) 或是 \(1, 4, n-5\)

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  int T = read();
  while (T --) {
    int n = read();
    if (n <= 6 || n == 9) {
      printf("NO\n");
      continue;
    }
    int x = 1, y = 2;
    n -= 3;
    if (n % 3 == 0) y = 4, n -= 2;
    printf("YES\n%d %d %d\n", x, y, n);
  }
  return 0;
}
/*
1 2 4

1 2 5


1 2 7
1 2 8
1 4 7
*/

B

二分圆的半径,依次检查 O 和 P 是否在某个圆中、是否在同一个圆中、两个圆是否相交即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const double eps = 1e-12;
//=============================================================
int p[2], a[2], b[2];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
bool Check(double w_) {
  double dw = w_;
  double da0 = sqrt(1.0 * a[0] * a[0] + 1.0 * a[1] * a[1]);
  double db0 = sqrt(1.0 * b[0] * b[0] + 1.0 * b[1] * b[1]);
  
  double dap = sqrt(1.0 * (a[0] - p[0]) * (a[0] - p[0]) + (a[1] - p[1]) * (a[1] - p[1]));
  double dbp = sqrt(1.0 * (b[0] - p[0]) * (b[0] - p[0]) + (b[1] - p[1]) * (b[1] - p[1]));
  
  if (da0 > dw && db0 > dw) return false;
  if (dap > dw && dbp > dw) return false;
  
  if (da0 <= dw && dap <= dw) return true;
  if (db0 <= dw && dbp <= dw) return true;

  double dab = sqrt(1.0 * (a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]));

  if (dab > 2.0 * dw) return false;

  return true;
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  int T = read();
  while (T --) {
    for (int i = 0; i < 2; ++ i) p[i] = read();
    for (int i = 0; i < 2; ++ i) a[i] = read();
    for (int i = 0; i < 2; ++ i) b[i] = read();

    double l = 0, r = 1e10, ans;
    while (r - l > eps) {
      double mid = (l + r) / 2.0;
      if (Check(mid)) {
        ans = mid;
        r = mid;
      } else {
        l = mid;
      }
    }
    printf("%.12lf\n", ans);
  }
  return 0;
}

C

捏吗赛时首先进行了一个心的贪假了 WA 了 2 发我草然后又想了一个贪心但是还是假了然后突然手玩出来了然后又 WA 了两发呃呃

大力手玩考虑构造字符串的过程是如何进行的。比如这组样例:xyaxa,一个显然的想法是每次操作都让所有的 a 都能前移一位,直到字符串第一个字符 a,然后再让第二个字符变成 a……然后再考虑这组样例:yxzaxa,发现删除顺序是先删 y 再删 z 再删 x……

可以发现每次都是删掉了当前字符串一段最长的不降的前缀末尾的字符,使得后面的更小的字符得以前移,直到删得整个字符串都是不降的,然后再从末尾往前删,于是就可以构造出字符串每一段的形态了。第一步每次仅关注当前位置和之后一个位置是否不降,如果降了再考虑当前位置的前一个和后一个位置即可,可以在顺序枚举字符串的同时用链表简单维护。

最多只会正序倒序枚举字符串各一次,总时间复杂度 \(O(n)\) 级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e6 + 10;
//=============================================================
int n, pre[kN], next[kN];
LL pos;
char s[kN];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
void Init() {
  scanf("%s", s + 1); n = strlen(s + 1);
  for (int i = 0; i <= n + 1; ++ i) {
    next[i] = i + 1;
    pre[i] = i - 1;
  }
  std::cin >> pos;
}
void Solve(LL now_, LL pos_) {
  for (int i = next[0]; i <= n; i = next[i]) {
    ++ now_;
    if (now_ == pos_) {
      printf("%c", s[i]);
      return ;
    }
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  int T = read();
  while (T --) {
    Init();

    LL now = 0, r = n, flag = 0;
    for (int i = 0; i < n; ) {
      if (flag == 1) break;
      if (i == 0) i = next[i];
      if (next[i] == n + 1) break;

      if (s[i] > s[next[i]]) {
        if (now + 1ll * r >= pos) {
          Solve(now, pos), flag = 1;
          break;
        }
        next[pre[i]] = next[i], pre[next[i]] = pre[i];
        now += 1ll * r;
        i = pre[i];
        -- r;
        continue;
      }
      i = next[i];
    }
    if (flag == 1) continue;

    for (int i = r; i; -- i) {
      if (now + 1ll * i >= pos) {
        Solve(now, pos);
        break;
      }
      now += 1ll * i;
      pre[n + 1] = pre[pre[n + 1]];
      next[pre[n + 1]] = n + 1;
    }
  }
  return 0;
}
/*
1
abcd
9

1
xaya
5
*/

D

看起来很哈人实际上思路很水也很好写的题呃呃

我们考虑正序枚举给定的字符串 \(s\)仅关注元素间相对关系地依次新元素来构造合法排列。

\(f_{i}\) 表示构造出满足 \(s_1\sim s_{i - 1}\) 的合法的 \(p_{1}\sim p_i\) 的方案数。根据 \(s_{i-1}\) 考虑加入元素 \(p_i\) 后它在 \(p_1\sim p_i\) 中的排名。当 \(s_{i-1}\)<> 时,\(p_i\) 加入后的排名是唯一的,则 \(f_i = f_{i-1}\),否则 \(p_i\) 的排名可以是 \(2\sim i-1\) 中任意值,则有 \(f_i=f_{i-1}\times (i-2)\)。于是答案即:

\[\prod_{s_{i-1}=?, 2\le i\le n} (i-2) \]

因为只有单点修改,直接线性预处理逆元就能直接维护了。

根据上述过程和这个式子,可以发现当且仅当 \(s_1\not\)? 时无解,否则总能构造出合法答案,特判下即可。

总时间复杂度 \(O(n+m)\) 级别,牛逼。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 3e5 + 10;
const LL p = 998244353;
//=============================================================
int n, m, flag;
char s[kN], t[10];
LL inv[kN], ans = 1;
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
void Init() {
  n = read(), m = read();
  scanf("%s", s + 1);
  flag = s[1] != '?';
  for (int i = 2; i < n; ++ i) {
    ans = (ans * (s[i] == '?' ? i - 1 : 1)) % p;
  }

  inv[1] = 1;
  for (int i = 2; i <= n; ++ i) {
    inv[i] = 1ll * (p - p / i + p) % p * inv[p % i] % p;
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  Init();
  printf("%lld\n", flag ? ans : 0);
  while (m --) {
    int pos = read(); scanf("%s", t + 1);
    if (pos == 1) {
      flag = t[1] != '?';
    } else {
      ans = (ans * (s[pos] == '?' ? inv[pos - 1] : 1)) % p;
      ans = (ans * (t[1] == '?' ? pos - 1 : 1)) % p;
    }
    s[pos] = t[1];
    printf("%lld\n", flag ? ans : 0);
  }
  return 0;
}

写在最后

  • C:大力手玩,以及小心地证明。
  • D:一种基于考虑新加入的元素排名的排列计数套路。
posted @ 2023-10-10 17:31  Luckyblock  阅读(153)  评论(0编辑  收藏  举报