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 的数的集合,则显然有:
注意模数给定,组合数要杨辉三角求。
//
/*
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\) 的取值范围显然为 \([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:后缀前缀和均可相互转化。
参考: