C. Rotatable Array
首先 \(k\) 中只有 \(k\%N\) 次操作有用
执行 \(i\) 次操作后,点 \(p\) 位置上的数就会变成 \((p+i)\%N\) 位置上的数
对于操作 \(3\),用一个变量 offset 记录 \(k\%N\) 的累加和即可
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, q;
cin >> n >> q;
vector<int> a(n);
rep(i, n) a[i] = i+1;
int offset = 0;
rep(qi, q) {
int type;
cin >> type;
if (type == 3) {
int k;
cin >> k;
offset = (offset+k)%n;
}
else {
int p;
cin >> p;
--p;
int i = (offset+p)%n;
if (type == 1) {
int x;
cin >> x;
a[i] = x;
}
else {
cout << a[i] << '\n';
}
}
}
return 0;
}
D. XOR Shortest Walk
爆搜
记 dp[v][x] 表示走到点 \(v\) 且当前的异或和为 \(x\) 的路径是否存在
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
struct Edge {
int to, w;
};
int main() {
int n, m;
cin >> n >> m;
int W = 1<<10;
vector<vector<Edge>> g(n);
rep(i, m) {
int a, b, w;
cin >> a >> b >> w;
--a; --b;
g[a].emplace_back(b, w);
}
vector<bool> used(n*W);
queue<int> q;
auto push = [&](int v, int w) {
int vid = v*W+w;
if (used[vid]) return;
used[vid] = true;
q.push(vid);
};
push(0, 0);
while (q.size()) {
int vid = q.front(); q.pop();
int v = vid/W, x = vid%W;
for (auto [u, w] : g[v]) push(u, x^w);
}
rep(x, W) {
if (used[(n-1)*W+x]) {
cout << x << '\n';
return 0;
}
}
puts("-1");
return 0;
}
ps:换成无向图也是可以做的
E. Battles in a Row
简单dp
记 dp[i][j][k] 表示到第 \(i\) 只怪物为止,生命值剩下 \(j\) 点,魔法值剩下 \(k\) 点
状态数就是 \(O(NHM)\),显然超时
这个是个 bool 型的dp,取值为 0/1
考虑将第 \(3\) 维拿掉,记 dp[i][j] 表示到第 \(i\) 只怪物为止,生命值剩下 至少 \(j\) 点时剩余魔法值的最大值
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
inline void chmax(int& x, int y) { if (x < y) x = y; }
int main() {
int n, h, m;
cin >> n >> h >> m;
vector<int> dp(h+1, -1);
dp[h] = m;
rep(i, n) {
int a, b;
cin >> a >> b;
vector<int> old(h+1, -1);
swap(dp, old);
rep(j, h+1) if (old[j] != -1) {
if (a <= j) {
chmax(dp[j-a], old[j]);
}
if (b <= old[j]) {
chmax(dp[j], old[j]-b);
}
}
if (dp == vector<int>(h+1, -1)) {
cout << i << '\n';
return 0;
}
}
cout << n << '\n';
return 0;
}
ps:也可以加一个逃跑的行动
F. Balanced Rectangles
实际上就是求有多少个子矩阵的累加和为 \(0\)
一维问题很好做,二维可以将它拍扁就变成一维了(枚举两行,对于每一列只考虑它的累加和)
可以做到 \(O(H^2W)\),看上去似乎会超时,但实际上并不会
假设 \(H \leqslant W\),\(H^2 \leqslant HW \leqslant 3 \times 10^5=M\),\(\Rightarrow \ H \leqslant \sqrt{M}\)
那么 \(O(H^2W)=O(M\sqrt{M})\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
void solve() {
int h, w;
cin >> h >> w;
vector<string> s(h);
rep(i, h) cin >> s[i];
if (h > w) {
swap(h, w);
vector<string> old(h, string(w, '.'));
swap(s, old);
rep(i, h)rep(j, w) s[i][j] = old[j][i];
}
int n = h*w;
vector<int> cnt(n*2+1);
ll ans = 0;
rep(si, h) {
vector<int> a(w);
for (int ti = si; ti < h; ++ti) {
rep(j, w) a[j] += s[ti][j] == '#' ? 1 : -1;
vector<int> sum(w+1);
sum[0] = n;
rep(j, w) sum[j+1] = sum[j]+a[j];
rep(j, w) {
cnt[sum[j]]++;
ans += cnt[sum[j+1]];
}
rep(j, w) cnt[sum[j]] = 0;
}
}
cout << ans << '\n';
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
G. Longest Chord Chain
从某点切开后,这题就转化成了求解关于前缀和后缀的最大重叠层数问题。最长嵌套弦链,将每条弦按端点较大的那个点的编号做升序排序的话,就消除掉了一维,然后用线段树加速一下即可
代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
int op(int a, int b) { return max(a, b); }
int e() { return 0; }
int main() {
int n;
cin >> n;
vector<P> ps;
rep(i, n) {
int a, b;
cin >> a >> b;
--a; --b;
if (a > b) swap(a, b);
ps.emplace_back(b, a);
}
ranges::sort(ps);
int ans = 0;
// dp[r]:以 r 为最外层弦右端点的嵌套弦链的最大长度
// lis[l]:以 l 为最外层弦左端点的嵌套弦链的最大长度
segtree<int, op, e> lis(n*2), dp(n*2);
for (auto [r, l] : ps) {
int now = lis.prod(l, r)+1;
ans = max(ans, now+dp.prod(0, l));
dp.set(r, now);
lis.set(l, now);
}
cout << ans << '\n';
return 0;
}
浙公网安备 33010602011771号