首页 > 编程语言 >2024“钉耙编程”中国大学生算法设计超级联赛(1)

2024“钉耙编程”中国大学生算法设计超级联赛(1)

时间:2024-07-20 17:30:11浏览次数:17  
标签:钉耙 std begin int 编程 2024 ++ vector z2

发挥相当差,最好笑的是 1h 没写出一个三维偏序、30min 没写出一个字符串哈希。甚至 1h 没意识到组合数式子推错了。

A

我写了点阴间东西。

假设模式串为 ABC,考虑一个形如 ABCABCABC 的东西,如果长度是 \(x\),会贡献 \(x-n+1\) 个子串。

枚举 \(i\),从 \(i\) 把 \(T\) 分成两部分,一部分与 \(S\) 拼一起跑 Z 函数得到 \(T[i \dots n]\) 能与 \(S\) 的前缀匹配几位,再翻转 \(S\) 与 \(T\) 得到 \(T[i-1 \dots 0]\) 能与 \(T\) 的后缀从后面匹配几位,两边拼一起得到 BCABCABCABC.. 类似物的长短。

因为一次考虑 \(S\) 的很多次重复,开始要把 \(S\) 复制到长度不小于 \(T\)。

没解绑同步流过的。感觉第一发 Hash 被卡常可能真的是输入的锅。

#include <bits/stdc++.h>
#define LL long long
const int M = 2e7 + 5;
using string = std::string;
std::vector<int> getz(string s) {
  int n = s.length();
  std::vector<int> z(n);
  z[0] = n;
  int l = 0, r = 0;
  for (int i = 1; i < n; i++) {
    z[i] = i <= r ? std::min(r - i + 1, z[i - l]) : 0;
    while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
    if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
  }
  return z;
}
int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  int T; std::cin >> T; while (T--) {
    string s, t;
    std::cin >> s >> t;
    int n0 = s.length(), m = t.length(), n = n0;
    int x = m / n0 + 1;
    s.resize(n = n0 * x);
    for (int i = n0; i < n; i++) {
      s[i] = s[i - n0];
    }
    std::vector<int> z1 = getz(s + '@' + t);
    std::reverse(s.begin(), s.end()), std::reverse(t.begin(), t.end());
    auto z2 = getz(s + '@' + t);
    z1.erase(z1.begin(), z1.begin() + n + 1);
    z2.erase(z2.begin(), z2.begin() + n + 1);
    std::reverse(z2.begin(), z2.end());
    std::vector<int> z3(n);
    for (int i = 0; i < m; i++)
      z3[i - z2[i] + 1] = std::max(z3[i - z2[i] + 1], z2[i]);
    int ans = 0;
    int pre = -1;
    for (int i = 0; i < m; i++) {
      if (i + z1[i] - n0 + 1 > m || i + z1[i] - n0 + 1 < 0) continue;
      if (z1[i] + (i ? z2[i - 1] : 0) >= n0) {
        if (pre == i + z1[i]) continue;
        ans += z1[i] + (i ? z2[i - 1] : 0) - n0 + 1;
        pre = i + z1[i];
      }
    }
    std::cout << ans << "\n";
  }
  
}

B

朴素背包。

std 告诉我们先随机打乱,前 \(i\) 关期望获得 \(E=ik/n\) 颗星星,找一个阈值 \(B\),只更新 \([E-B, E+B]\) 内的 dp 值就行。

#include <bits/stdc++.h>

using LL = long long;

int main() {
  int t; scanf("%d", &t); while (t--) {
    int n, k; scanf("%d %d", &n, &k);
    std::vector<LL> f(k + 1, 1e17);
    f[0] = 0;
    for (int i = 0; i < n; i++) {
      std::vector<LL> g(f);
      for (int j = 1, t; j <= 4; j++) {
        scanf("%d", &t);
        for (int x = 0; x <= k - j; x++)
          g[x + j] = std::min(g[x + j], f[x] + t);
      }
      f.swap(g);
    }
    printf("%lld\n", f[k]);
  }
}

C

不是我开的。

\(\max(x, y) \cdot |x-y| = \max(x, y)^2 - \max(x, y) \min(x, y) = \max(x, y)^2 - xy\)。

后者是容易的,前者用树上启发式合并,\(O(n \log^2 n)\)。

队友赛时写了个线段树合并,好强啊。

D

晚点写。

E

若 \(S\) 为偶数,设平局概率为 \(p\),答案是 \((1-p)/2\)。

否则看一下前 \((n-1)/2\) 局就平的概率,如果平了是 A 赢,否则是 \(1/2\)。

如何算平局的概率?给每个字母标号,统计 \(A_i - B_i\) 有序对构成的可重集。因为可重,总方案数是 \(S!/(S/2)!\),而合法的方案数是 \(\prod c_i!/(c_i/2)!\)。

能算错平局的概率,脑子已经失去了。想钦定顺序,忘了两边并不等价。

#include <bits/stdc++.h>

// 省略 modint

int main() {
  initC(1e7 + 1);
  int T; scanf("%d", &T); while (T--) {
    int n; scanf("%d", &n);
    std::vector<int> c(26, 0);
    int sum = 0;
    modint ans = 1;
    for (int i = 0, x; i < n; i++) {
      char s; scanf(" %c %d", &s, &x);
      c[s - 'a'] += x, sum += x;
    }
    int cnt = 0;
    for (int i = 0; i < n; i++) if (c[i] % 2 == 1) ++cnt;
    if (cnt > 1) { printf("%d\n", (mod + 1) / 2); continue; }
    for (int i = 0; i < 26; i++) {
      ans *= fac[c[i]] * ifac[c[i] >> 1];
    }
    ans *= ifac[sum] * fac[sum >> 1];
    // printf("%d\n", ans);
    if (cnt == 0)
      printf("%d\n", (1 - ans) / 2);
    else 
      printf("%d\n", (1 + ans) / 2);
  }
}

F

赛时在想拆贡献 dp,越想越远。

但三次方有另一个拆贡献方法:挑三个位于该集合内的数的方案数。据此 dp 即可,前缀和优化转移。

G

竞赛图三元环的经典转化是:考虑非三元环,必存在一个点出度为 \(2\)。

三维偏序求每个点出度即可。

H

拆位即可。

I

假设已经确定了数字串,考虑 dp:\(f_{i, j, k}\) 表示扫完了数字串前 \(i\) 位,匹配模式串到第 \(j\) 位,且最后一位为 \(k\) 的方案数。

数位 dp 会把答案转化为:\(n |\sum|\) 次先确定前面若干位,再让后面随便填。

前半相当于数字串已经被确定,对于后半,\(f_{0/1, i, j, k}\) 表示前面是否全部是前导零,还能填 \(i\) 位,匹配模式串到第 \(j\) 位,最后一位为 \(k\) 的方案数,暴力合并。

#include <bits/stdc++.h>

const int mod = 998244353;

using v2 = std::vector<std::vector<int>>;
using v3 = std::vector<v2>;
using v4 = std::vector<v3>;

void solve() {
  std::string l, r, s;  std::cin >> l >> r >> s;
  std::reverse(r.begin(), r.end());
  r = r + '0';
  for (int i = 0; i < (int)r.length(); i++) {
    if (r[i] < '9') { ++r[i]; break; }
    r[i] = '0'; 
  }
  while (r.length() && r.back() == '0') r.pop_back();
  std::reverse(l.begin(), l.end());
  while (l.length() < r.length()) l.push_back('0');
  std::reverse(r.begin(), r.end());
  std::reverse(l.begin(), l.end());
  // std::cerr << l << " " << r << std::endl;

  int n = r.length(), m = s.length();
  v4 f(2, v3(n + 1, v2(m + 2, std::vector<int>(10, -1))));
  // 是否全是 0 T 匹配到第 p 位 子序列选出了 q 个数 最后一个数为 x
  std::function<int(int, int, int, int)> dfs = [&](int b, int p, int q, int x) {
      // std::cerr << b << " " << p << " " << q << " " << x << std::endl;
    int &ans = f[b][p][q][x];
    if (ans != -1) return ans;
    if (p == n) return ans = (q == m + 1);
    if (q == m + 1) return ans = 10ll * dfs(0, p + 1, q, x) % mod;
    ans = 0;
    for (int y = 0; y < 10; y++) {
      (ans += dfs(b && !y, p + 1, q, x)) %= mod;
      if (b && !y) continue;
      if (q && s[q - 1] == '<' && x >= y) continue;
      if (q && s[q - 1] == '>' && x <= y) continue;
      (ans += dfs(0, p + 1, q + 1, y)) %= mod;
    }
    return ans;
  };
  auto solve = [&](std::string x) {
    int n = x.length();
    auto p = x;
    // 已经匹配模式串的前 i 位,最后一位为 j
    v2 f(n + 1, std::vector<int>(10, 0));
    // 在 f 后面放一个 y
    auto nxt = [&](int y) {
      auto g = f;
      ++g[0][y];
      for (int i = 0; i < m; i++) {
        for (int x = 0; x < 10; x++) {
          if (s[i] == '<' && x >= y) continue;
          if (s[i] == '>' && x <= y) continue;
          (g[i + 1][y] += f[i][x]) %= mod;
        }
      }
      return g;
    };
    int ans = 0;
    bool c = 1;
    int i = 0; while (x[i] == '0') ++i;
    for (; i < n; i++) {
      int t = x[i] - '0';
      for (int y = 0; y < t; y++) {
        (ans += dfs(c && !y, i + 1, 0, 0)) %= mod;
        if (c && !y) continue;
        auto g = nxt(y);
        for (int j = 0; j <= m; j++) 
          for (int x = 0; x < 10; x++) if (g[j][x]) {
            (ans += 1ll * g[j][x] * dfs(0, i + 1, j + 1, x) % mod) %= mod;
          }
      }
      c &= (x[i] == '0');
      f = nxt(t);
    }
    return ans;
  };
  int R = solve(r);
  f.assign(2, v3(n + 1, v2(m + 2, std::vector<int>(10, -1))));
  // std::cout << R << " " << solve(l) << "\n";
  std::cout << (R - solve(l) + mod) % mod << "\n";
}

int main() {
  std::ios::sync_with_stdio(0);
  int T; std::cin >> T; while (T--) {
    solve();
  }
}

J

从大到小枚举众数。开一个优先队列,

标签:钉耙,std,begin,int,编程,2024,++,vector,z2
From: https://www.cnblogs.com/purplevine/p/18313459

相关文章

  • 2024牛客暑期多校训练营2 HI
    2024牛客暑期多校训练营2H.InstructionsSubstring题意:有一个字符串序列,有WSAD四种操作,可以上下左右移动。可以选取一段连续的子序列,从(0,0)出发,经过连续子序列操作后可以经过点(x,y),问这样的子序列有多少个思路:若一个子序列能够实现到达点(x,y),那么在这个子序列后面加任意字符都......
  • 2024 暑假友谊赛 2
    B.TilingChallenge1.我的方法是按顺序遍历,遇到'.'时就检查一下它的上下左右是不是都是点,如果都是点的话,标记这个点,把这个点和他上下左右都标记为‘?’,但是要加一个条件,如果‘.’的个数不是5的倍数就不符合题意,不加这个会wa37,我也不知道为什么#include<bits/stdc++.h>#defin......
  • NOI 2024 退役记
    NOI2024结束了,我的OI生涯也告一段落了。怎么说呢,今年变动挺大的,Day1之前确实完全没有做好准备,考场策略全乱套了,最后Day1考151分,直接垫底了,T1是个小难写的ds,我做法反正很难写且常数大,最后调完了也没卡常卡过去,拿了80,还浪费了巨大长时间,场上看到有了pretest我也很......
  • 2024牛客暑期多校训练营2
    H.InstructionsSubstring题目大意:给出一段长为\(n\)的字符串,其中WASD分别代表向上下左右走,给出目的地\((x,y)\),选择一段连续子序列使得从\((0,0)\)出发可以经过目的地,求这样的子序列的总数。思路:用前缀和记录到\(i\)为止到达的位置,从前往后遍历右端点\(r\),找到恰好到......
  • Java中的异步编程与CompletableFuture应用
    Java中的异步编程与CompletableFuture应用大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代Java编程中,异步编程变得越来越重要,它可以帮助我们提高应用程序的响应速度和性能。CompletableFuture是Java8引入的一个强大工具,它简化了异步编程,使得......
  • 深入理解面向对象编程(OOP)
    ......
  • 2024杭电第一场
    1012并考虑两维坐标都离散化后枚举每个格子,用二维前缀和计算其被覆盖的次数,设为cnt则k固定时该格子的贡献为:(1-C[n-cnt][k]/C[n][k])*该格子的面积注意事项:1.处处取模2.给的是点坐标,不是格点坐标,因此计算二维前缀和时要x++,y++#include<bits/stdc++.h>usingnames......
  • NOI2024退役记
    本文仍在不断更新中不知不觉我的OI生涯就结束了,走到这一步才突然发觉。才想起来我还有好多感兴趣的内容没学,还有好多有趣的题没做,还有好多流逝的时间没有抓住。但是都已经走到这一步了,高中的OI之旅就也只能到此为止了。Day-?提前两周来到了重庆,跟着梦熊的模拟赛,又认识了一个......
  • 【2024-ZR-C Day 4】图论(1)
    1.强连通分量1.1.定义在有向图中,选取一个点集\(S\),若对于\(S\)中的任意两点\(u,v\),都满足\(u\)可以到达\(v\),则称\(S\)是强连通的。强连通分量是图中一个极大的强连通的点集。性质:把一个有向图通过强连通分量缩点后,新的图是一个DAG.1.2.Kosaraju算法在无向图......
  • C基础(学习)2024.7.19
             Linux基本命令,vi编译器的使用,简单的编程步骤,程序语言,gcc编译器编译过程,进制转换相关知识可以查看文档http://t.csdnimg.cn/CmqhC        数值表示,词法符号,变量,常量相关知识可以查看文档http://t.csdnimg.cn/jJIe2        运算符和输表达式......