Codeforces Round 925 (Div. 3)

写在前面

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

本来想给小号上波大分,但是上线一看之前一场 div2 回滚了突然暴涨 60 分直接蓝了,没的上分了哈哈。

另外这场糖丸了真是呃呃,F 死活不过赛后 10min 换了个思路立马过了,妈的

还是打会儿天使骚骚 REBOOT 吧家人们,唉飞舞一个没有活着的资格

隔壁家的大哥哥情人节凌晨还在写代码,写完代码就去玩 galgame 和纸片人约会去了,你可千万不要变得和他一样啊.jpg

A

三重循环暴力枚举即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    int x; std::cin >> x;
    int flag = 0;
    for (int i = 'a'; i <= 'z'; ++ i) {
      if (flag) break;
      for (int j = 'a'; j <= 'z'; ++ j) {
        if (flag) break;
        for (int k = 'a'; k <= 'z'; ++ k) {
          if (flag) break;
          if (i + j + k - 'a' - 'a' - 'a' + 3 == x) {
            flag = 1;
            std::cout << (char) i << (char) j << (char) k << "\n";
          } 
        }
      }
    }
  }
  return 0;
}

B

发现只能从前往后倒水,一个显然的想法时应当尽可能地将之前部分中剩余的水倒到后面。考虑归纳法,设已知前缀 \(i-1\) 合法的情况下,检查用前 \(i-1\) 中剩余部分加上 \(a_i\) 能否使前缀 \(i\) 合法。

即检查对于 \(1\le i\le n\),是否满足:

\[\sum_{j=1}^{i} a_j \ge i\times \frac{\sum_{j=1}^{n}a_j}{n} \]

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int a[kN];
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    int n; std::cin >> n;
    int flag = 1;
    LL sum1 = 0, sum2 = 0;
    for (int i = 1; i <= n; ++ i) {
      std::cin >> a[i];
      sum1 += a[i];
    }
    for (int i = 1; i <= n; ++ i) {
      sum2 += a[i];
      if (sum2 < 1ll * sum1 / n * i) flag = 0;
    }
    std::cout << (flag ? "YES\n" : "NO\n");
  }
  return 0;
}

C

显然答案有单调性,考虑二分答案,检查答案不大于枚举量 \(mid\) 时是否合法,于是枚举所有长度为 \(mid\) 的区间判断是否存在某个区间,使其补集中元素种类数不大于 1。

枚举实现即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, ans, a[kN];
int num, cnt[kN];
//=============================================================
void add(int x_) {
  if (cnt[x_] == 0) ++ num;
  ++ cnt[x_];
}
void del(int x_) {
  -- cnt[x_];
  if (cnt[x_] == 0) -- num;
}
bool Check(int mid_) {
  for (int i = mid_ + 1; i <= n; ++ i) add(a[i]);
  int flag = 0;
  for (int l = 1, r = mid_; r <= n; ++ l, ++ r) {
    if (num <= 1) flag = 1;
    if (r != n) add(a[l]), del(a[r + 1]);
  }
  for (int i = 1; i <= n - mid_; ++ i) del(a[i]);
  return flag;
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n;
    for (int i = 1; i <= n; ++ i) std::cin >> a[i];
    for (int l = 0, r = n; l <= r; ) {
      int mid = (l + r) / 2;
      if (Check(mid)) {
        ans = mid;
        r = mid - 1;
      } else {
        l = mid + 1;
      }
    }
    std::cout << ans << "\n";
  }
  return 0;
}

D

对于数列中的某种权值 \(p\),发现能与其凑成一对的权值 \(q\) 一定满足:

\[\begin{cases} q \bmod x &= (x - p)\bmod x\\ q\bmod y &= p\bmod y \end{cases}\]

于是考虑枚举数列元素,用 map 记录每种数对 \((a_i\bmod x, a_i\bmod y)\) 的数量,检查数对 \(((a_i - p)\bmod x, a_i\bmod y)\) 的数量并累加即为答案。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
#define pii std::pair<int,int>
#define mp std::make_pair
const int kN = 2e5 + 10;
//=============================================================
int n, x, y, a[kN];
LL ans;
//=============================================================
void Solve() {
  ans = 0;
  std::map <pii, int> m;
  for (int i = 1; i <= n; ++ i) {
    std::cin >> a[i];
    ans += m[mp((x - a[i] % x) % x, a[i] % y)];
    ++ m[mp(a[i] % x, a[i] % y)];
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n >> x >> y;
    Solve();
    std::cout << ans << "\n";
  }
  return 0;
}
/*
9 5 6
10 7 6 7 9 7 7 10 10
*/

E

题目要求等价于判断最终剩下的数的位数是否不小于 \(m+1\)

Anna 仅会对每个数操作至多一次,操作的结果相当于删去该数的后缀零;Sasha 操作的结果相当于将两个数合并,合并时可以选择保留哪个数的后缀零;发现仅需关注输入的每个数的位数与其后缀零的数量即可。

发现两人操作的顺序显然是贪心的:Anna 和 Sasha 每次均会选择后缀零最多的数进行操作,优先队列模拟即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
#define pii std::pair<int,int>
#define mp std::make_pair
const int kN = 2e5 + 10;
//=============================================================
int n, m, a[kN];
//=============================================================
bool Solve() {
  std::priority_queue <pii> q;
  for (int i = 1; i <= n; ++ i) {
    std::string s; std::cin >> s;
    int x = 0;
    for (int i = s.length() - 1; i >= 0; -- i) {
      if (s[i] != '0') break;
      ++ x;
    }
    q.push(mp(x, s.length()));
  }
  
  while (!q.empty()) {
    pii now = q.top(); q.pop();
    q.push(mp(0, now.second - now.first));
    if (q.size() < 2) break;

    pii now1 = q.top(); q.pop();
    pii now2 = q.top(); q.pop();
    q.push(mp(now2.first, now1.second + now2.second));
  }
  return q.top().second >= m + 1;
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n >> m;
    std::cout << (Solve() ? "Sasha\n" : "Anna\n");
  }
  return 0;
}

F

对于每张截图通过已知的后 \(n-1\) 个人的顺序建有向图拓扑排序即可,有解等价于无环。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
const int kM = kN << 3;
//=============================================================
int n, k, a[kN];
int edgenum, head[kN], v[kM], ne[kM];
int into[kN];
//=============================================================
void Init() {
  edgenum = 0;
  for (int i = 1; i <= n; ++ i) head[i] = into[i] = 0;
}
void Add(int u_, int v_) {
  v[++ edgenum] = v_;
  ne[edgenum] = head[u_];
  head[u_] = edgenum;

  ++ into[v_];
}
bool Solve() {
  for (int i = 1; i <= k; ++ i) {
    for (int i = 1; i <= n; ++ i) {
      std::cin >> a[i];
      if (i >= 3) Add(a[i - 1], a[i]);
    }
  }
  std::queue <int> q;
  for (int i = 1; i <= n; ++ i) {
    if (!into[i]) q.push(i);
  }
  while (!q.empty()) {
    int u_ = q.front(); q.pop();
    for (int i = head[u_]; i; i = ne[i]) {
      if ((-- into[v[i]]) == 0) q.push(v[i]);
    }
  }
  for (int i = 1; i <= n; ++ i) {
    if (into[i]) return 0;
  }
  return 1;
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n >> k;
    Init();
    std::cout << (Solve() ? "YES\n" : "NO\n");
  }
  return 0;
}

下面是赛时折磨一个多小时赛后十分钟过的写的一坨屎,傻逼还不知道对不对而且跑得超级慢的做法但是过了,扔在这里算了呃呃:

\(k=1\) 必有解;\(k=2\) 时仅需检查除截图者之外的人的相对顺序即可。手玩下发现当 \(k=2\) 时无法确定两张截图的截图者的相对顺序从而无法唯一确定所有人的顺序;再手玩下发现 \(k\ge 3\) 时即可唯一确定所有人的顺序。考虑通过前三张截图,得到其中某截图者的前驱后继,再基于他的截图构造出所有人的顺序。

通过观察可以发现,若可以通过前三张截图构造出所有人的顺序,则存在唯一的某个截图者,满足在另外两人的截图中的位置不同(至多相差 1,说明他在三人的相对顺序中一定是在中间的),则他的后继位置靠前的截图中的后继前驱位置靠后的截图中的前驱。于是检查能否构造出所有人的顺序,再检查其他截图是否均合法即可。

写得好丑呃呃

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, k, a[kN], b[kN], c[kN];
int pre[kN], next[kN];
//=============================================================
bool Solve() {
  int flag = 1;
  for (int i = 1; i <= n; ++ i) std::cin >> a[i];
  if (k == 1) return 1;
  //=============================================================
  for (int i = 1; i <= n; ++ i) std::cin >> b[i];
  for (int i = 2, j = 2; i <= n && j <= n; ) {
    if (a[i] == b[1]) {
      ++ i;
      continue;
    }
    if (b[j] == a[1]) {
      ++ j;
      continue;
    }
    if (a[i] != b[j]) {
      flag = 0;
      break;
    }
    ++ i, ++ j;
  }
  if (k == 2) return flag;
  //=============================================================
  for (int i = 1; i <= n; ++ i) std::cin >> c[i];
  for (int i = 2, j = 2; i <= n && j <= n; ) {
    if (a[i] == c[1]) {
      ++ i;
      continue;
    }
    if (c[j] == a[1]) {
      ++ j;
      continue;
    }
    if (a[i] != c[j]) {
      flag = 0;
      break;
    }
    ++ i, ++ j;
  }
  for (int i = 2, j = 2; i <= n && j <= n; ) {
    if (b[i] == c[1]) {
      ++ i;
      continue;
    }
    if (c[j] == b[1]) {
      ++ j;
      continue;
    }
    if (b[i] != c[j]) {
      flag = 0;
      break;
    }
    ++ i, ++ j;
  }
  //=============================================================
  int p1 = 0, p2 = 0, cnt = 0;
  for (int i = 2; i <= n; ++ i) {
    if (b[i] == a[1]) p1 = i;
    if (c[i] == a[1]) p2 = i;
  }
  if (p1 != p2) ++ cnt;

  for (int i = 2; i <= n; ++ i) {
    if (a[i] == b[1]) p1 = i;
    if (c[i] == b[1]) p2 = i;
  }
  if (p1 != p2) ++ cnt;

  p1 = 0, p2 = 0;
  for (int i = 2; i <= n; ++ i) {
    if (a[i] == c[1]) p1 = i;
    if (b[i] == c[1]) p2 = i;
  }
  if (p1 != p2) ++ cnt;
  if (cnt > 1) flag = 0;
  //=============================================================
  for (int i = 2; i <= n; ++ i) {
    if (a[i] == b[1]) p1 = i;
    if (c[i] == b[1]) p2 = i;
  }
  if (p1 != p2) std::swap(b, a);

  p1 = 0, p2 = 0;
  for (int i = 2; i <= n; ++ i) {
    if (a[i] == c[1]) p1 = i;
    if (b[i] == c[1]) p2 = i;
  }
  if (p1 != p2) std::swap(c, a);

  pre[a[1]] = next[a[1]] = -1;
  p1 = 0, p2 = 0;
  for (int i = 2; i <= n; ++ i) {
    if (b[i] == a[1]) p1 = i;
    if (c[i] == a[1]) p2 = i;
  }
  if (p1 > p2) std::swap(b, c), std::swap(p1, p2);
  pre[a[1]] = c[p1], next[a[1]] = b[p2];
  //=============================================================
  std::vector <int> d;
  d.push_back(0);
  if (pre[a[1]] == 0) d.push_back(a[1]), pre[a[1]] = next[a[1]] = -1;
  for (int i = 2; i <= n; ++ i) {
    if (next[a[1]] == a[i]) d.push_back(a[1]), pre[a[1]] = next[a[1]] = -1;
    d.push_back(a[i]);
    if (pre[a[1]] == a[i]) d.push_back(a[1]), pre[a[1]] = next[a[1]] = -1;
  }
  if (next[a[1]] == n + 1) d.push_back(a[1]);
  //=============================================================
  for (int r = 4; r <= k; ++ r) {
    for (int i = 1; i <= n; ++ i) std::cin >> b[i];
    for (int i = 1, j = 2; i <= n && j <= n; ) {
      if (d[i] == b[1]) {
        ++ i;
        continue;
      }
      if (d[i] != b[j]) {
        flag = 0;
        break;
      }
      ++ i, ++ j;
    }
  }
  return flag;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n >> k;
    std::cout << (Solve() ? "YES\n" : "NO\n");
  }
  return 0;
}
/*
5 4
3 5 1 4 2
2 5 1 4 3
1 5 4 3 2
5 1 4 3 2

5 1 4 3 2



1
3 3
2 1 3
1 2 3
3 2 1

2 1 3


3 3
1 3 2
2 1 3
3 2 1
*/

G

大力讨论组合数学题。

考虑从左向右添加形状,则仅需关注此时最右端的凹凸性即可。发现形状 3,4 加入后不会使凹凸性变化,则忽略法链中的形状 3,4 后链中一定为形状 1,2 交替排列的形式。于是考虑构造形状 1,2 的交替排列后插入形状 3,4。发现此时形状 3,4 只能不断地插入到形状 1 的右侧/左侧,相当于向一列无编号的盒子中放任意个球的模型。

然后开始大力讨论:

  • 若形状 1,2 不存在:当且仅当仅有 3 或 4 时答案为 1,否则为 0。
  • 否则当 \(|c_1 - c_2|> 1\) 时答案为 0。
  • 否则考虑形状 1,2 交替排列的形式:
    • \(c_1 = c_2\),则既可以是 1212...12 也可以是 2121...21
      • 若为前者则对于形状 3 有 \(c_1\) 个位置(所有形状 1 的右侧)可以插入,对于形状 4 有 \(c_1 + 1\) 个位置(所有形状 1 的左侧及最后一个形状 2 的右侧)插入,方案数为:\({{c_1 + c_3 - 1}\choose{c_1 - 1}}\times {{c_1 + c_4}\choose{c_1}}\)
      • 若为后者则对于形状 3 有 \(c_1+1\) 个位置可以插入,对于形状 4 有 \(c_1\) 个位置插入,方案数为:\({{c_1 + c_3}\choose{c_1}}\times {{c_1 + c_4-1}\choose{c_1-1}}\)
    • \(c_1 = c_2 + 1\) 则只能是 1212...121,对于形状 3,4 均有 \(c_1\) 个位置可以插入,方案数为:\({{c_1 + c_3 - 1}\choose{c_1 - 1}}\times {{c_1 + c_4 - 1}\choose{c_1 - 1}}\)
    • \(c_2 = c_1 + 1\) 则只能是 2121...212,对于形状 3,4 均有 \(c_1+1\) 个位置可以插入,方案数为:\({{c_1 + c_3}\choose{c_1}}\times {{c_1 + c_4}\choose{c_1}}\)
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e6 + 10;
const LL p = 998244353;
//=============================================================
int c[5];
LL fac[kN], ifac[kN];
//=============================================================
LL qpow(LL x_, LL y_) {
  LL ret = 1;
  while (y_) {
    if (y_ & 1) ret = ret * x_ % p;
    x_ = x_ * x_ % p, y_ >>= 1ll;
  }
  return ret;
}
void Init(int n_ = kN - 10) {
  fac[0] = 1;
  for (int i = 1; i <= n_; ++ i) fac[i] = fac[i - 1] * i % p;
  ifac[n_] = qpow(fac[n_], p - 2);
  for (int i = n_ - 1; i >= 0; -- i) ifac[i] = ifac[i + 1] * (i + 1) % p;
}
LL C(LL x_, LL y_) {
  return fac[x_] * ifac[x_ - y_] % p * ifac[y_] % p;
}   
LL Solve() {
  if (c[1] == 0 && c[2] == 0) return (c[3] == 0) || (c[4] == 0);
  if (abs(c[1] - c[2]) > 1) return 0;
  LL ret = 0;
  if (c[1] == c[2]) {
    ret += C(c[1] + c[3] - 1, c[1] - 1) * C(c[2] + c[4], c[2]) % p;
    ret += C(c[1] + c[3], c[1]) * C(c[2] + c[4] - 1, c[2] - 1) % p;
    ret %= p;
  }
  if (c[1] > c[2]) {
    ret = C(c[1] + c[3] - 1, c[1] - 1) * C(c[1] + c[4] - 1, c[1] - 1) % p;
  }
  if (c[2] > c[1]) {
    ret = C(c[1] + c[3], c[1]) * C(c[1] + c[4], c[1]) % p;
  }
  return ret;
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  Init();
  while (T --) {
    for (int i = 1; i <= 4; ++ i) std::cin >> c[i];
    std::cout << Solve() << "\n";
  }
  return 0;
}

写在最后

  • F:拓扑序!
posted @ 2024-02-14 01:50  Luckyblock  阅读(119)  评论(0编辑  收藏  举报