C. Distance Indicators
\(j-A_j = A_i+i\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
int n;
cin >> n;
vector<int> a(n);
rep(i, n) cin >> a[i];
map<int, int> cnt;
rep(i, n) cnt[i+a[i]]++;
ll ans = 0;
rep(i, n) ans += cnt[i-a[i]];
cout << ans << '\n';
return 0;
}
D. Takahashi's Expectation
如果 \(x \leqslant 1000\),可以直接dp解决
记 dp[i][j] 表示从接收第 \(i\) 个礼物开始,当前情绪值为 \(j\) 时,向后接收所有礼物后的最终的情绪值,其中 \(j \leqslant 1000\)(因为 \(p_i\) 的上界是 \(500\))
需要从后往前进行转移
最后的答案为 dp[0][x]
如果 \(x > 1000\) 时应该如何处理?
容易发现,当 \(x \geqslant 500\) 时,情绪一定会一直减少,还是因为 \(p_i\) 的上界是 \(500\),所以只会触发减少的操作
令 \(S_i = b_0 + b_1 + \cdots + b_{i-1}\)
二分找到 \(S\) 中满足 \(x-S_i \leqslant 1000\) 的最小的 \(i\)
然后讨论一下:
如果 \(i < n\),答案为 dp[i][x-S[i]]
否则,答案为 x-S[i](已经收完了所有礼物)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
vector<int> p(n), a(n), b(n);
rep(i, n) cin >> p[i] >> a[i] >> b[i];
const int m = 1001;
vector dp(n+1, vector<int>(m));
rep(i, m) dp[n][i] = i;
for (int i = n-1; i >= 0; --i) {
rep(j, m) {
int nj;
if (j <= p[i]) nj = j+a[i]; else nj = max(0, j-b[i]);
dp[i][j] = dp[i+1][nj];
}
}
vector<int> bs(n+1);
rep(i, n) bs[i+1] = bs[i]+b[i];
int q;
cin >> q;
rep(qi, q) {
int x;
cin >> x;
int ans;
if (x >= m) {
int i = upper_bound(bs.begin(), bs.begin()+n, x-m)-bs.begin();
x -= bs[i];
if (i < n) ans = dp[i][x];
else ans = x;
}
else ans = dp[0][x];
cout << ans << '\n';
}
return 0;
}
E. A Path in A Dictionary
从点 \(x\) 出发,每次走到能走到点 \(y\) 的点中编号最小的相邻节点
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
void solve() {
int n, m, x, y;
cin >> n >> m >> x >> y;
n++;
vector<vector<int>> to(n);
rep(i, m) {
int a, b;
cin >> a >> b;
to[a].push_back(b);
to[b].push_back(a);
}
vector<bool> used(n);
vector<int> ans(1, x);
used[x] = true;
while (x != y) {
vector<bool> reach(n);
queue<int> q;
reach[y] = true; q.push(y);
while (q.size()) {
int v = q.front(); q.pop();
for (int u : to[v]) {
if (used[u]) continue;
if (reach[u]) continue;
reach[u] = true;
q.push(u);
}
}
int nx = n;
for (int v : to[x]) {
if (used[v]) continue;
if (!reach[v]) continue;
nx = min(nx, v);
}
x = nx;
used[x] = true;
ans.push_back(x);
}
n = ans.size();
rep(i, n) cout << ans[i] << " \n"[i == n-1];
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
F. Random Gathering
对于每次操作,区间 \([l, r]\) 上每个位置的石子数量的期望值都为 \(\dfrac{\sum\limits_{i=l}^r A_i}{r-l+1}\)
于是问题就变成一个“区间赋值,区间求和”的线段树问题了
代码实现
#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 mint = modint998244353;
struct S { int w; mint sum; };
S op(S a, S b) { return S(a.w+b.w, a.sum+b.sum); }
S e() { return S(0, 0); }
struct F { bool change; mint x; };
S mapping(F f, S x) {
if (!f.change) return x;
return S(x.w, f.x*x.w);
}
F composition(F f, F g) {
if (!f.change) return g;
return f;
}
F id() { return F(false, 0); }
int main() {
int n, m;
cin >> n >> m;
vector<S> a(n);
rep(i, n) {
int x;
cin >> x;
a[i] = S(1, x);
}
lazy_segtree<S, op, e, F, mapping, composition, id> t(a);
rep(mi, m) {
int l, r;
cin >> l >> r;
--l;
S s = t.prod(l, r);
mint ave = s.sum/s.w;
t.apply(l, r, F(true, ave));
}
rep(i, n) cout << t.get(i).sum.val() << ' ';
return 0;
}
G. Binary Cat
启发式合并
考虑从后往前算答案
每次维护一个集合,代表后面那些串要在这里算答案,并且要求第多少位
然后就可以把在 \(l\) 的和在 \(r\) 的分裂出去
另一种做法是倍增
虽然可以像ABC380D那样处理,但如果每个查询问的是“将 \(S_i\) 和 \(S_0\) 拼接后,第 \(1\) 个字符是什么?”,时间复杂度会达到 \(O(Q^2)\)。
然而,如果像ABC380D那样字符串长度每次减半,就能在 \(O(\log N)\) 时间内解决。因此,我们只需通过倍增预处理,将“\(S_{L_i}\) 和 \(S_{R_i}\) 规约到较长字符串的情况”统一处理,就能高效回溯查询结果!
浙公网安备 33010602011771号