AtCoder Beginner Contest 417
A - A Substring
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, a, b;
std::cin >> n >> a >> b;
std::string s;
std::cin >> s;
std::cout << s.substr(a, n - a - b) << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
B - Search and Delete
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> a(n), b(m);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
for (int i = 0; i < m; ++ i) {
std::cin >> b[i];
}
std::map<int, int> cnt;
for (auto & x : b) {
++ cnt[x];
}
for (int i = 0; i < n; ++ i) {
if (cnt[a[i]] == 0) {
std::cout << a[i] << " ";
} else {
-- cnt[a[i]];
}
}
std::cout << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
C - Distance Indicators
题意:给你一个数组\(a\),求有多少\(i, j\)满足\(i < j\)且\(j - i = a_i + a_j\)。
式子可以变为:\(j - a_j = a_i + i\)。
那么用\(map\)记录前缀\(a_i + i\)的个数就行。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::map<int, int> cnt;
i64 ans = 0;
for (int i = 0; i < n; ++ i) {
ans += cnt[i - a[i]];
++ cnt[a[i] + i];
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
D - Takahashi's Expectation
题意:有\(n\)个三元组\((p_i, a_i, b_i)\)。开始有一个\(x\),然后从前往后遍历这些三元组,如果\(p_i \geq x\),\(x\)变为\(x+a_i\),否则\(x\)变为\(\max(0, x - b_i)\)。\(q\)次询问,每次给定\(x\),求其最后变成的数。\(p_i, a_i, b_i \leq 500\)。
观察到三元组的范围都很小,如果\(x \leq 500\)我们进行模拟,如果\(x > 500\),那么它要一直减\(b_i\)直到小于等于\(500\),可以二分找到这个位置,然后让这个询问从这个位置开始。
也就是我们可以离线查询,每个\(x\)有一个起点和在这个起点的值,于是可以从前往后遍历三元组,每次把以这个位置为起点的询问加进来操作就行。可以用并查集维护每个值对应的查询编号,当两个查询在同一个位置的值一样的时候,我们合并它们,最后每个询问找到自己的祖先的值就是答案。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
struct DSU {
std::vector<int> fa, cnt;
DSU();
DSU(int n) {
init(n);
}
void init(int n) {
fa.assign(n + 1, 0);
cnt.assign(n + 1, 1);
std::iota(fa.begin(), fa.end(), 0);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool merge(int x, int y) {
int u = find(x), v = find(y);
if (u == v) {
return false;
}
fa[v] = u;
cnt[u] += cnt[v];
return true;
}
bool same(int u, int v) {
return find(u) == find(v);
}
int size(int u) {
return cnt[find(u)];
}
};
void solve() {
int n;
std::cin >> n;
std::vector<int> p(n), a(n), b(n);
for (int i = 0; i < n; ++ i) {
std::cin >> p[i] >> a[i] >> b[i];
}
std::vector<int> sum(n);
sum[0] = b[0];
for (int i = 1; i < n; ++ i) {
sum[i] = sum[i - 1] + b[i];
}
int q;
std::cin >> q;
std::vector<std::vector<std::pair<int, int>>> add(n);
std::vector<int> ans(q);
for (int i = 0; i < q; ++ i) {
int x;
std::cin >> x;
if (x <= 500) {
add[0].emplace_back(x, i);
} else {
if (x - 500 > sum[n - 1]) {
ans[i] = x - sum[n - 1];
} else {
int p = std::ranges::lower_bound(sum, x - 500) - sum.begin();
add[p].emplace_back(x - (p > 0 ? sum[p - 1] : 0), i);
}
}
}
std::array<int, 1001> f{};
f.fill(-1);
DSU dsu(q);
for (int i = 0; i < n; ++ i) {
for (auto & [x, j] : add[i]) {
if (f[x] != -1) {
dsu.merge(f[x], j);
} else {
f[x] = j;
}
}
std::array<int, 1001> g{};
g.fill(-1);
for (int x = 0; x <= 1000; ++ x) {
if (f[x] != -1) {
int y = p[i] >= x ? x + a[i] : std::max(0, x - b[i]);
if (g[y] != -1) {
dsu.merge(g[y], f[x]);
} else {
g[y] = f[x];
}
}
}
f = g;
}
for (int i = 0; i <= 1000; ++ i) {
if (f[i] != -1) {
ans[f[i]] = i;
}
}
for (int i = 0; i < q; ++ i) {
ans[i] = ans[dsu.find(i)];
}
for (int i = 0; i < q; ++ i) {
std::cout << ans[i] << "\n";
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
E - A Path in A Dictionary
题意:给你一个图,求从\(x\)到\(y\)的路径中进过点的编号的序列的字典序最小的路径。
每个点都优先走编号最小的邻点。\(dfs\)一遍就行。
因为想要字典序最小肯定前面的位置选小的数更好,那么优先走小的点,只有这个点不通的情况才走其它点中最小点,因为能保证前面的数尽可能小,所以这样是字典序最小的。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, m, x, y;
std::cin >> n >> m >> x >> y;
-- x, -- y;
std::vector<std::vector<int>> adj(n);
for (int i = 0; i < m; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
for (int i = 0; i < n; ++ i) {
std::ranges::sort(adj[i]);
}
std::vector<int> pre(n, -1);
auto dfs = [&](auto & self, int u) -> void {
for (auto & v : adj[u]) {
if (pre[v] == -1) {
pre[v] = u;
self(self, v);
}
}
};
pre[x] = x;
dfs(dfs, x);
std::vector<int> ans;
while (y != x) {
ans.push_back(y);
y = pre[y];
}
ans.push_back(x);
std::ranges::reverse(ans);
for (auto & x : ans) {
std::cout << x + 1 << " \n"[x == ans.back()];
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
F - Random Gathering
题意:一个数组初始为\(a_i\),有\(m\)个操作,每次把\([l_i, r_i]\)之间的和记为\(s\),然后把\([l_i, r_i\)的数都变成\(0\)。最后把\(s\)随机赋值给\([l_i, r_i]\)的一个数。最后求每个位置的期望。
如果\(p_i\)表示当前\(i\)的期望,那么对于一个操作\([l, r]\),和的期望就是期望的和,也就是\(sum = \sum_{i=l}^{r} p_i\),那么每个位置的期望就是\(\frac{sum}{r-l+1}\)。
用可以维护区间赋值和区间求和的线段树就行。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
template<class T>
constexpr T power(T a, i64 b) {
T res = 1;
for (; b; b /= 2, a *= a) {
if (b % 2) {
res *= a;
}
}
return res;
}
constexpr i64 mul(i64 a, i64 b, i64 p) {
i64 res = a * b - i64(1.L * a * b / p) * p;
res %= p;
if (res < 0) {
res += p;
}
return res;
}
template<i64 P>
struct MLong {
i64 x;
constexpr MLong() : x{} {}
constexpr MLong(i64 x) : x{norm(x % getMod())} {}
static i64 Mod;
constexpr static i64 getMod() {
if (P > 0) {
return P;
} else {
return Mod;
}
}
constexpr static void setMod(i64 Mod_) {
Mod = Mod_;
}
constexpr i64 norm(i64 x) const {
if (x < 0) {
x += getMod();
}
if (x >= getMod()) {
x -= getMod();
}
return x;
}
constexpr i64 val() const {
return x;
}
explicit constexpr operator i64() const {
return x;
}
constexpr MLong operator-() const {
MLong res;
res.x = norm(getMod() - x);
return res;
}
constexpr MLong inv() const {
assert(x != 0);
return power(*this, getMod() - 2);
}
constexpr MLong &operator*=(MLong rhs) & {
x = mul(x, rhs.x, getMod());
return *this;
}
constexpr MLong &operator+=(MLong rhs) & {
x = norm(x + rhs.x);
return *this;
}
constexpr MLong &operator-=(MLong rhs) & {
x = norm(x - rhs.x);
return *this;
}
constexpr MLong &operator/=(MLong rhs) & {
return *this *= rhs.inv();
}
friend constexpr MLong operator*(MLong lhs, MLong rhs) {
MLong res = lhs;
res *= rhs;
return res;
}
friend constexpr MLong operator+(MLong lhs, MLong rhs) {
MLong res = lhs;
res += rhs;
return res;
}
friend constexpr MLong operator-(MLong lhs, MLong rhs) {
MLong res = lhs;
res -= rhs;
return res;
}
friend constexpr MLong operator/(MLong lhs, MLong rhs) {
MLong res = lhs;
res /= rhs;
return res;
}
friend constexpr std::istream &operator>>(std::istream &is, MLong &a) {
i64 v;
is >> v;
a = MLong(v);
return is;
}
friend constexpr std::ostream &operator<<(std::ostream &os, const MLong &a) {
return os << a.val();
}
friend constexpr bool operator==(MLong lhs, MLong rhs) {
return lhs.val() == rhs.val();
}
friend constexpr bool operator!=(MLong lhs, MLong rhs) {
return lhs.val() != rhs.val();
}
};
template<>
i64 MLong<0LL>::Mod = i64(1E18) + 9;
template<int P>
struct MInt {
int x;
constexpr MInt() : x{} {}
constexpr MInt(i64 x) : x{norm(x % getMod())} {}
static int Mod;
constexpr static int getMod() {
if (P > 0) {
return P;
} else {
return Mod;
}
}
constexpr static void setMod(int Mod_) {
Mod = Mod_;
}
constexpr int norm(int x) const {
if (x < 0) {
x += getMod();
}
if (x >= getMod()) {
x -= getMod();
}
return x;
}
constexpr int val() const {
return x;
}
explicit constexpr operator int() const {
return x;
}
constexpr MInt operator-() const {
MInt res;
res.x = norm(getMod() - x);
return res;
}
constexpr MInt inv() const {
assert(x != 0);
return power(*this, getMod() - 2);
}
constexpr MInt &operator*=(MInt rhs) & {
x = 1LL * x * rhs.x % getMod();
return *this;
}
constexpr MInt &operator+=(MInt rhs) & {
x = norm(x + rhs.x);
return *this;
}
constexpr MInt &operator-=(MInt rhs) & {
x = norm(x - rhs.x);
return *this;
}
constexpr MInt &operator/=(MInt rhs) & {
return *this *= rhs.inv();
}
friend constexpr MInt operator*(MInt lhs, MInt rhs) {
MInt res = lhs;
res *= rhs;
return res;
}
friend constexpr MInt operator+(MInt lhs, MInt rhs) {
MInt res = lhs;
res += rhs;
return res;
}
friend constexpr MInt operator-(MInt lhs, MInt rhs) {
MInt res = lhs;
res -= rhs;
return res;
}
friend constexpr MInt operator/(MInt lhs, MInt rhs) {
MInt res = lhs;
res /= rhs;
return res;
}
friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
i64 v;
is >> v;
a = MInt(v);
return is;
}
friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
return os << a.val();
}
friend constexpr bool operator==(MInt lhs, MInt rhs) {
return lhs.val() == rhs.val();
}
friend constexpr bool operator!=(MInt lhs, MInt rhs) {
return lhs.val() != rhs.val();
}
};
template<>
int MInt<0>::Mod = 998244353;
template<int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();
constexpr int P = 998244353;
using Z = MInt<P>;
template <class Info, class Tag>
struct LazySegmentTree {
struct Node {
int l, r;
Info info;
Tag tag;
};
std::vector<Node> tr;
LazySegmentTree() {}
LazySegmentTree(int n) {
init(n);
}
void init(int n) {
tr.assign(n << 2, {});
build(0, n - 1);
}
void pushdown(Node & u, const Tag & tag) {
u.info = u.info + tag;
u.tag = u.tag + tag;
}
void pushdown(int u) {
if (tr[u].tag.exist()) {
pushdown(tr[u << 1], tr[u].tag);
pushdown(tr[u << 1 | 1], tr[u].tag);
tr[u].tag.clear();
}
}
void pushup(int u) {
tr[u].info = tr[u << 1].info + tr[u << 1 | 1].info;
}
void build(int l, int r, int u = 1) {
tr[u] = {l, r, {}};
if (l == r) {
return;
}
int mid = l + r >> 1;
build(l, mid, u << 1); build(mid + 1, r, u << 1 | 1);
pushup(u);
}
void modify(int l, int r, const Tag & tag, int u = 1) {
if (l <= tr[u].l && tr[u].r <= r) {
pushdown(tr[u], tag);
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) {
modify(l, r, tag, u << 1);
}
if (r > mid) {
modify(l, r, tag, u << 1 | 1);
}
pushup(u);
}
Info query(int l, int r, int u = 1) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u].info;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (r <= mid) {
return query(l, r, u << 1);
} else if (l > mid) {
return query(l, r, u << 1 | 1);
}
return query(l, r, u << 1) + query(l, r, u << 1 | 1);
}
};
struct Info {
Z sum, len = 1;
};
struct Tag {
Z set;
bool exist() {
return set != 0;
}
void clear() {
set = 0;
}
};
Info operator + (const Info & a, const Info & b) {
Info res{};
res.sum = a.sum + b.sum;
res.len = a.len + b.len;
return res;
}
Info operator + (const Info & a, const Tag & b) {
Info res{};
res.sum = b.set * a.len;
res.len = a.len;
return res;
}
Tag operator + (const Tag & a, const Tag & b) {
Tag res{};
res = b;
return res;
}
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
LazySegmentTree<Info, Tag> tr(n);
for (int i = 0; i < n; ++ i) {
tr.modify(i, i, Tag{(Z)a[i]});
}
for (int i = 0; i < m; ++ i) {
int l, r;
std::cin >> l >> r;
-- l, -- r;
auto [sum, len] = tr.query(l, r);
tr.modify(l, r, Tag{sum / len});
}
for (int i = 0; i < n; ++ i) {
std::cout << tr.query(i, i).sum << " \n"[i == n - 1];
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}