小猫 round 比赛总结

分数:\(50 + 90 + 0 + 45 = 185\)

梗最多的一集。

T1

神秘 ad-hoc,把我和 AeeE5x 坑惨了,反而让 hiphop 捞着了。

这道题中对贡献的描述乍一看有些莫名其妙,但实际上,就是一旦在序列中添加一个端点,那么贡献就会加上此处的后缀和。所以,我们只需要贪心地取前 \(k\) 个最大且 \(>0\) 的贡献即可。

#include <bits/stdc++.h>
#define int long long
const int N = 1e5+10;
int T, n, k, suf[N]; std::string s;

signed main() {
  freopen("sail.in", "r", stdin);
  freopen("sail.out", "w", stdout);
  std::ios::sync_with_stdio(false); std::cin.tie(0);
  std::cin >> T;
  while (T--) {   
    std::cin >> n >> k >> s;
    s = " " + s;
    suf[n+1] = 0;
    for (int i = n; i >= 1; i--) {
      suf[i] = suf[i+1] + (s[i] == '1' ? 1 : -1);
    }
    std::priority_queue<int> q;
    for (int i = n; i > 1; i--) {
      q.push(suf[i]);
    }
    int sum = 0, ans = 0;
    while (!q.empty()) {
      if (q.top() <= 0) break;
      sum += q.top();
      q.pop();
      ans++;
      if (sum >= k) break;
    }
    if (sum < k) std::cout << -1 << '\n';
    else std::cout << ans + 1 << '\n';
  }  
  return 0;
}

T2

这道题场上把最难的部分攻下了,却忽略了 \(n = 1\) 这一最简单的情况,与 AC 失之交臂。一定要特判 \(n = 1\),原因稍后会讲。

以编号记录节点不太直观,我们考虑以数对 \((a, b)\) 记录节点,表示这是第 \(a\) 层从左往右数第 \(b\) 个节点,其中根节点的层数记为 \(0\)

可以推出,节点 \((a, b)\) 的编号为 \(\sum_{i = 0}^{a - 1}n^i + b\)

小科普

\[\sum\limits_{i=0}^{m}n^i = \frac{n^{m+1}-1}{n-1} \]

这会用于我们稍后的代码中。从中也能看到,当 \(n = 1\) 时,\(0\) 会成为分母(由于要取模,在代码中实际表现为求 \(0\) 的逆元),这就是为什么要对 \(n=1\) 的情况进行特判。

考虑到 \((a, b)\) 左边的 \((b-1)\) 个节点分别都有 \(n\) 个儿子,因此节点 \((a, b)\) 的第 \(c\) 个儿子可以表示为 \((a+1, (b-1)n + c)\)。但是本题的难点在于如何求走 \(y\) 步后到达的节点。其层数显然为 \(a+y\),我们只讨论其在一层的位置 \(b_y\)。设 \(f(b, c) = (b-1)n+c\),则

\[\begin{align*} b_2 &= f(f(b, c)) \\ &= f((b-1)n+c)\\ &= (bn-n+c-1)n+c\\ &= bn^2-n^2+cn-n+c\\ b_3 &= f(f(f(b, c)))\\ &= f(bn^2-n^2+cn-n+c)\\ &= (bn^2-n^2+cn-n+c-1)n+c\\ &= bn^3-n^3+cn^2-n^2+cn-n+c\\ b_4 &= bn^4-n^4+cn^3-n^3+cn^2-n^2+cn-n+c\\ &\cdots\\ b_y &= bn^y-n^y+cn^{y-1}-n^{y-1}+cn^{y-2}-n^{y-2}+ \cdots +cn-n+c\\ &= bn^y-n^y+\sum\limits_{i=1}^{y-1}(c-1)n^i+c \end{align*} \]

还剩最后一个难点:如何求节点 \((a, b)\)\(y\) 级祖先?如果这道题没有取模,直接对 \(n\) 进行对 \(n\) 取模相关操作即可,但是考虑到此题要对 \(998244353\) 取模,直接求解比较困难。不过我们有一种间接求解的方式 —— “回望来路”。

在我们刚才求儿子的时候,我们每从一个节点出发,就把它连同此时的方向压入一个栈中。当我们要求解祖先时,可以不停地弹出栈顶元素,直到其深度小于要求解祖先的深度,这意味着我们找到了一个我们曾经走过的,且比要求的点高的节点 \((a', b')\)。于是我们就把求解 \((a, b)\) 的祖先转化成了求 \((a', b')\) 的后代。注意方向为此前记录的方向。

#include <bits/stdc++.h>
#define int long long

const int N = 1e5+10, MOD = 998244353;
int n, q;

int qpow(int a, int b) {
  int res = 1;
  while (b) {
    if (b & 1) res = res * a % MOD;
    a = a * a % MOD;
    b >>= 1;
  }
  return res;
}

void exgcd(int a, int b, int &x, int &y) {
  if (!b) {x = 1, y = 0; return;}
  else exgcd(b, a%b, y, x), y -= a / b * x;
}

int getInv(int a) {
  int x = 0, y = 0;
  exgcd(a, MOD, x, y);
  return x % MOD;
}

int jiheJishu(int n, int m) {
  return ((qpow(n, m + 1) - 1) * getInv(n - 1)) % MOD;
}

int rangeJHJS(int a, int b) {
  if (a == 0) return jiheJishu(n, b);
  return (jiheJishu(n, b) - jiheJishu(n, a-1) % MOD + MOD) % MOD;
}

struct Node {
  int level, id;
};

int NodeToNum(Node node) {
  return ((rangeJHJS(0, node.level - 1) + node.id) % MOD + MOD) % MOD;
}

Node getSon(Node ori, int c, int y) {
  if (y == 0) return ori;
  int a = ori.level, b = ori.id;
  Node res;
  res.level = a + y;
  res.id = (qpow(n, y) * b) % MOD - qpow(n, y) + ((c - 1) * (rangeJHJS(1, y - 1))) % MOD + c;
  if (res.id < 0) res.id += MOD;
  return res;
}

std::stack<std::pair<Node, int> > stk;

signed main() {
  freopen("tree.in", "r", stdin);
  freopen("tree.out", "w", stdout);
  std::ios::sync_with_stdio(false); std::cin.tie(0);

  std::cin >> n >> q;

  if (n == 1) {
    int curr = 1;
    while (q--) {
      int x, y;
      std::cin >> x >> y;
      if (x == 0) {
        curr -= y;
      } else {
        curr += y;
      }
      std::cout << curr << '\n';
    }
    return 0;
  }

  Node curr = {0, 1};
  while (q--) {
    int x, y;
    std::cin >> x >> y;
    if (x == 0) {
      while (stk.top().first.level > curr.level - y) {
        stk.pop();
      }
      curr = getSon(stk.top().first, stk.top().second, curr.level - y - stk.top().first.level);
      std::cout << NodeToNum(curr) << '\n';
      continue;
    }
    stk.push({curr, x});
    curr = getSon(curr, x, y);
    std::cout << NodeToNum(curr) << '\n';
  }

  return 0;
}
posted @ 2025-11-19 16:30  JZ8  阅读(4)  评论(0)    收藏  举报