2024牛客暑期多校训练营1

写在前面

比赛地址:https://ac.nowcoder.com/acm/contest/81596

以下按个人向难度排序。

4h 调一题而且没调出来之我是超级红温怪

赛后换了个写法 30min 重构一发改了改过了我草,好相似。

单刷卡题太难顶!

H

经典阅读理解签到。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
//=============================================================
int n, m;
struct group {
  std::string name;
  int p, t;
} a[kN], b[kN];
std::map <std::string, int> vis;
std::map <std::string, int> rank1, rank2;
//=============================================================
bool cmp(group fir_, group sec_) {
  if (fir_.p != sec_.p) return fir_.p > sec_.p;
  return fir_.t < sec_.t;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> n;

  for (int i = 1; i <= n; ++ i) {
    std::cin >> a[i].name >> a[i].p >> a[i].t;
    vis[a[i].name] = vis[a[i].name] + 1;
  }
  std::sort(a + 1, a + n + 1, cmp);
  for (int i = 1; i <= n; ++ i) rank1[a[i].name] = i;

  std::cin >> m;
  for (int i = 1; i <= m; ++ i) {
    std::cin >> b[i].name >> b[i].p >> b[i].t;
    vis[b[i].name] = vis[b[i].name] + 1;
  }
  std::sort(b + 1, b + m + 1, cmp);
  for (int i = 1; i <= m; ++ i) rank2[b[i].name] = i;

  int rk1 = rank1["lzr010506"], rk2 = rank2["lzr010506"];
  int ans1 = rk1, ans2 = rk2;
  for (int i = 1; i < rk1; ++ i) {
    if (vis[a[i].name] == 2) -- ans1;
  }
  for (int i = 1; i < rk2; ++ i) {
    if (vis[b[i].name] == 2) -- ans2;
  }

  std::cout << std::min(ans1, ans2) << "\n";
  return 0;
}

C

签到。

模拟即可,增删权值 \(v_i\) 的影响即 \(\plusmn i\times v_i\)

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const LL p = 1e9 + 7;
//=============================================================
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int q; std::cin >> q;

  std::stack<int> st;
  LL s = 0;
  while (q --) {
    int t, v; std::cin >> t >> v;
    for (int i = 1; i <= t; ++ i) {
      s = (s - (1ll * st.top() * st.size()) % p + p) % p;
      st.pop();
    }
    st.push(v);
    s = (s + 1ll * st.size() * v % p) % p;
    std::cout << s << "\n";
  }
  return 0;
}

A

组合。

dztlb 大神直接秒了。

发现若存在一个子序列使得该子序列按位与为 1,则该子序列所有数第 0 位均为 1,且可知所有第 0 位为 1 的数(即奇数)按位与一定也为 1。于是仅需考虑所有奇数按位与为 1 的数列数量即可。

考虑枚举数列奇数的数量。设有 \(i\) 个奇数,\(n - i\) 个偶数,则偶数其他各位任意,奇数中剩余 \(m - 1\) 位上至少有一个数该位为 0。考虑枚举奇数的每位上为 0 的数的集合,则显然有:

\[\operatorname{Ans} = \sum_{i = 1}^{n} {n\choose i}\times {(2^{m - 1})}^{n - i}\times (2^{i} - 1) ^ {m - 1} \]

注意模数给定,组合数要杨辉三角求。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 5020;
//=============================================================
LL n, m, q, c[kN][kN];
//=============================================================
LL qpow(LL x_, LL y_) {
  LL ret = 1ll;
  while (y_) {
    if (y_ & 1) ret = ret * x_ %q;
    x_ = x_ * x_ % q, y_ >>= 1ll;
  }
  return ret;
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> n >> m >> q;

  c[0][0] = 1;
  for (int i = 1; i <= n; ++ i) {
    c[i][0] = c[i][i] = 1;
    for (int j = 1; j < i; ++ j) {
      c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % q;
    }
  }

  LL ans = 0;
  for (int i = 1; i <= n; ++ i) {
    LL v = c[n][i];
    v = c[n][i] * qpow(2, (m - 1) * (n - i)) % q;
    v = v * qpow(qpow(2, i) - 1, m - 1) % q;
    ans = (ans + v) % q;
  }
  std::cout << ans << "\n";
  return 0;
}

I

模拟,图论(?

没想好就直接开写导致红温 4h 我是超级大傻逼。

考虑到反射方向是唯一的,手玩下发现图中所有光路只有两种情况:一直绕圈或者经过至多一次路径翻转后射出,且这两种光路之间没有重叠,即不存在先经过一段路径后再到达圈里开始绕圈的情况。发现这个光线位置和方向的转换挺图论的,于是考虑对每个位置拆成四个点,分别表示从该位置射出的朝向四个方向的光线,按照光线射出和反射方向建图。

考虑到两种光路之间没有重叠,则建图后一定是若干互不相交的链和简单环。链中每个点与出度为 0 点(即射出迷宫边缘的状态)的路径即为从该点射入时的光路,环同理。于是仅需直接大力统计统计每个环,以及每条链上各点到出度为 0 的点的路径上不同镜子的数量,即可预处理出所有询问的答案。考虑到光路可逆的性质,从射出迷宫边缘的状态(即出度为 0 点)反向搜索即可处理所有链,再枚举所有未被统计的状态即可处理所有环。

由于这个图很简单,实现时并不需要显式地建图,在给定地图上大力搜索即可。预处理后即可 \(O(1)\) 地回答询问。

总时间复杂度 \(O(nm + q)\) 级别。

妈的用 map 狂 T,改成数组就秒过。妈的能用数组我当初为什么要写 map 真是糖丸了、、、

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
#define pr std::pair
#define mp std::make_pair
#define piii std::pair<int,std::pair<int,int> >
const int kN = 1e6 + 10;
//=============================================================
int n, m;
std::string map[kN];
int ans[kN][4], tag[kN][4];
std::set<int> mirrors;
std::map<std::string, int> direction;
//=============================================================
piii mp3(int x_, int y_, int z_) {
  return mp(x_, mp(y_, z_));
}
int oppo(int dir_) {
  if (dir_ == 0) return 1;
  if (dir_ == 1) return 0;
  if (dir_ == 2) return 3;
  return 2;
}
int id(int x_, int y_) {
  return m * x_  + y_;
}
void dfs1(int x_, int y_, int dir_) {
  if (x_ < 0 || x_ >= n || y_ < 0 || y_ >= m) return ;

  ans[id(x_, y_)][oppo(dir_)] = mirrors.size();

  int nx, ny, now_reflected = 1, ndir;

  if (map[x_][y_] == '|') {
    if (dir_ == 0) nx = x_ - 1, ny = y_, ndir = 0, now_reflected = 0;
    else if (dir_ == 1) nx = x_ + 1, ny = y_, ndir = 1, now_reflected = 0;
    else if (dir_ == 2) nx = x_, ny = y_ + 1, ndir = 3;
    else if (dir_ == 3) nx = x_, ny = y_ - 1, ndir = 2;
  } else if (map[x_][y_] == '-') {
    if (dir_ == 0) nx = x_ + 1, ny = y_, ndir = 1;
    else if (dir_ == 1) nx = x_ - 1, ny = y_, ndir = 0;
    else if (dir_ == 2) nx = x_, ny = y_ - 1, ndir = 2, now_reflected = 0;
    else if (dir_ == 3) nx = x_, ny = y_ + 1, ndir = 3, now_reflected = 0;
  } else if (map[x_][y_] == '/') {
    if (dir_ == 0) nx = x_, ny = y_ + 1, ndir = 3;
    else if (dir_ == 1) nx = x_, ny = y_ - 1, ndir = 2;
    else if (dir_ == 2) nx = x_ + 1, ny = y_, ndir = 1;
    else if (dir_ == 3) nx = x_ - 1, ny = y_, ndir = 0;
  } else if (map[x_][y_] == '\\') {
    if (dir_ == 0) nx = x_, ny = y_ - 1, ndir = 2;
    else if (dir_ == 1) nx = x_, ny = y_ + 1, ndir = 3;
    else if (dir_ == 2) nx = x_ - 1, ny = y_, ndir = 0;
    else if (dir_ == 3) nx = x_ + 1, ny = y_, ndir = 1;
  }
  if (now_reflected && !mirrors.count(id(x_, y_))) {
    mirrors.insert(id(x_, y_));
  }
  dfs1(nx, ny, ndir);
}
void solve1(int x_, int y_, int dir_) {
  mirrors.clear();
  dfs1(x_, y_, dir_);
}

void dfs2(int x_, int y_, int dir_, int start_, int reflected_) {
  if (x_ < 0 || x_ >= n || y_ < 0 || y_ >= m) exit(0);

  int nx, ny, now_reflected = 1, ndir;
  if (dir_ == 0) nx = x_ - 1, ny = y_;
  if (dir_ == 1) nx = x_ + 1, ny = y_;
  if (dir_ == 2) nx = x_, ny = y_ - 1;
  if (dir_ == 3) nx = x_, ny = y_ + 1;
  if (0 <= nx && nx < n && 0 <= ny && ny < m) {
    if (map[nx][ny] == '|') {
      if (dir_ == 0) ndir = 0, now_reflected = 0;
      else if (dir_ == 1) ndir = 1, now_reflected = 0;
      else if (dir_ == 2) ndir = 3;
      else if (dir_ == 3) ndir = 2;
    } else if (map[nx][ny] == '-') {
      if (dir_ == 0) ndir = 1;
      else if (dir_ == 1) ndir = 0;
      else if (dir_ == 2) ndir = 2, now_reflected = 0;
      else if (dir_ == 3) ndir = 3, now_reflected = 0;
    } else if (map[nx][ny] == '/') {
      if (dir_ == 0) ndir = 3;
      else if (dir_ == 1) ndir = 2;
      else if (dir_ == 2) ndir = 1;
      else if (dir_ == 3) ndir = 0;
    } else if (map[nx][ny] == '\\') {
      if (dir_ == 0) ndir = 2;
      else if (dir_ == 1) ndir = 3;
      else if (dir_ == 2) ndir = 0;
      else if (dir_ == 3) ndir = 1;
    }
  }
  if (reflected_ && !mirrors.count(id(x_, y_))) {
    mirrors.insert(id(x_, y_));
  }
  if (tag[id(x_, y_)][dir_] != 0) {
    ans[id(x_, y_)][dir_] = mirrors.size();
    return ;
  }
  if (!start_) tag[id(x_, y_)][dir_] = 1;

  dfs2(nx, ny, ndir, 0, now_reflected);
  ans[id(x_, y_)][dir_] = mirrors.size();
}
void solve2(int x_, int y_, int dir_) {
  mirrors.clear();
  dfs2(x_, y_, dir_, 1, 0);
}
void init() {
  for (int i = 0; i < n; ++ i) solve1(i, 0, 3);
  for (int i = 0; i < n; ++ i) solve1(i, m - 1, 2);
  for (int j = 0; j < m; ++ j) solve1(0, j, 1);
  for (int j = 0; j < m; ++ j) solve1(n - 1, j, 0);

  for (int i = 0; i < n; ++ i) {
    for (int j = 0; j < m; ++ j) {
      for (int k = 0; k < 4; ++ k) {
        if (ans[id(i, j)][k] != -1) continue;
        solve2(i, j, k);
      }
    }
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  direction["above"] = 0;
  direction["below"] = 1;
  direction["left"] = 2;
  direction["right"] = 3;

  std::ios::sync_with_stdio(0), std::cin.tie(0);

  // dfs2(-1, -1, -1, -1, -1);

  std::cin >> n >> m;
  for (int i = 0; i < n; ++ i) std::cin >> map[i];
  for (int i = 0; i < n; ++ i) {
    for (int j = 0; j < m; ++ j) {
      for (int k = 0; k < 4; ++ k) {
        ans[id(i, j)][k] = -1;
      }
    }
  }
  init();

  int q; std::cin >> q;
  while (q --) {
    int x, y; std::string dir; std::cin >> x >> y >> dir;
    std::cout << ans[id(x - 1, y - 1)][direction[dir]] << "\n";
  }
  return 0;
}
/*
2 3
/-\
--/
2
1 2 right
2 1 right

3 4
/-\\
|/\|
\-/|
3
1 1 right
1 2 right
2 2 below

3 4
/-\\
|/\|
\-/|
3
1 1 right
1 2 right
2 2 below

3 4
/-\\
|/\|
\-/|
1
1 1 right

3 3
/\-
|||
\/|
1
1 1 right

3 3
--\
|||
--/
1
1 1 right

4 5
/---\
|/-\|
|\-/|
\---/
1
2 2 right
*/

D

二进制,数据结构。

发现模数是 \(2^{21}\)。这太几把二进制了!

增删一个数之后会影响到所有后缀,这太几把麻烦了,于是考虑能否转化成影响较小的形式进行维护。

于是想到转换成前缀的形式。记前缀和 \(\operatorname{pre}_i = \sum_{1\le j\le i} a_i\),则每次操作相当于增删 \(\operatorname{pre}_n\),查询 \(\oplus (\operatorname{pre}_n - \operatorname{pre}_i) \bmod 2^{21}\),即仅需按位考虑 \(\operatorname{pre}_n - \operatorname{pre}_i\) 的低 21 位即可。

先考虑如何回答询问。考虑拆位。设当前枚举到第 \(d(0\le d\le 20)\) 位,对于 $1\le i\le $ 求 \(\operatorname{pre}_n - \operatorname{pre}_i\) 中有多少个数该位为 1,即是否有:

\[\operatorname{pre}_n \bmod\ 2^{d + 1} - \operatorname{pre}_i \bmod\ 2^{d + 1} \ge 2^d \]

满足上式的 \(\operatorname{pre}_n \bmod\ 2^{d + 1} - \operatorname{pre}_i\) 的取值范围显然为 \([2^{d}, 2^{d + 1} - 1]\)。询问时 \(p = \operatorname{pre}_n \bmod\ 2^{d + 1}\) 为定值,做个差即可发现满足上述条件的 \(\operatorname{pre}_i \bmod\ 2^{d + 1}\) 的取值范围只有两种情况:

  • 为一段连续的区间 \([(2^d - p)\bmod\ 2^{d + 1}, (2^{d + 1} - 1 - p)\bmod\ 2^{d + 1}]\)
  • 为两段连续的区间 \([0, (2^{d + 1} - 1 - p)\bmod\ 2^{d + 1}]\)\([(2^d - p)\bmod\ 2^{d + 1}, 2^{d + 1} - 1]\)

仅需查询上述权值区间内的 \(\operatorname{pre}_i \bmod\ 2^{d + 1}\) 的个数即可。则仅需对于每一个 \(\operatorname{pre}_i\),对于 \(0\le d\le 20\),权值树状数组维护 \(\operatorname{pre}_i \bmod\ 2^{d + 1}\) 的权值的个数即可,修改仅需单点修改即可。

则总时间复杂度 \(O(q\log^2 v)\) 级别,有 \(O(\log v) = 21\)

注意维护的权值中可能有 0,树状数组要加个偏移量。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
#define low(x) ((x)&(-x))
const int kN = 5e5 + 10;
const int kM = 2097152 + 10;
//=============================================================
LL sum[kN];
//=============================================================
struct Bit {
  int lim, t[kM];
  void init(int lim_) {
    lim = lim_ + 1;
  }
  void insert(int pos_, int val_) {
    pos_ = pos_ + 1;
    for (int i = pos_; i <= lim; i += low(i)) t[i] += val_;
  }
  int sum(int pos_) {
    pos_ = pos_ + 1;

    int ret = 0;
    for (int i = pos_; i; i-= low(i)) ret += t[i];
    return ret;
  }
  int query(int l_, int r_) {
    return sum(r_) - sum(l_ - 1);
  }
} bit[21];
void modify(LL pos_, int val_) {
  for (int i = 0; i <= 20; ++ i) {
    int j = (1 << (i + 1));
    bit[i].insert((j - pos_ % j) % j, val_);
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int q; std::cin >> q;
  for (int i = 0; i <= 20; ++ i) bit[i].init((1 << (i + 1)));

  int n = 0;
  while (q --) {
    int t, v; std::cin >> t >> v;
    for (int i = 1; i <= t; ++ i) modify(sum[n - 1], -1), -- n;
    sum[n + 1] = sum[n] + v, ++ n;
    modify(sum[n - 1], 1);

    int ans = 0;
    for (int i = 0; i <= 20; ++ i) {
      int j = (1 << (i + 1)), l = (1 << i), r = j - 1, cnt = 0;
      l = (l - sum[n] % j + j) % j, r = (r - sum[n] % j + j) % j;
      
      if (l <= r) cnt = bit[i].query(l, r);
      else cnt = bit[i].sum(r) + bit[i].query(l, j - 1);
      if (cnt % 2 == 1) ans |= (1 << i);
    }
    std::cout << ans << "\n";
  }
  return 0;
}

B

组合。

写在最后

学到了什么:

  • I:想好在写!
  • D:后缀前缀和均可相互转化。

参考:

posted @ 2024-07-17 01:09  Luckyblock  阅读(249)  评论(2编辑  收藏  举报