[ABC 346] UNIQUE VISION Programming Contest 2024 Spring 题解
[ABC 346] UNIQUE VISION Programming Contest 2024 Spring 题解
A
模拟即可。
B
注意到子串一定有一部分是完整包含原串的,枚举散块的大小,然后判断是否剩下的可以组成若干原串即可。
string s = " wbwbwwbwbwbwwbwbwwbwbwbw";
for(int i = 1; i <= n; i ++) {
for(int j = i; j <= n; j ++) {
int x = w, y = b;
for(int k = i; k <= j; k ++) {
if(s[k] == 'w') x --;
else y --;
}
if(x / 7 == y / 5 && x % 7 == 0 && y % 5 == 0) return cout << "Yes\n", 0;
}
}
C
正难则反,统计区间内出现的整数和即可。
int ans = k * (k + 1) / 2;
for(int i = 1; i <= n; i ++) {
if(a[i] <= k && !h.count(a[i])) ans -= a[i];
h[a[i]] = 1;
}
D
考虑 DP,\(f_{i, 0/1, 0/1}\) 表示前 \(i\) 个位置,有没有相邻相同对,当前位置放0/1。
转移显然,basecase 要思考一下。
f[1][0][s[1] - '0'] = 0, f[1][0][(s[1] - '0') ^ 1] = c;
for(int i = 2; i <= n; i ++) {
cin >> c;
for(int a = 0; a < 2; a ++) {
for(int b = 0; b < 2; b ++) {
if(a == b) f[i][1][a] = min(f[i - 1][0][b] + (a != s[i] - '0') * c, f[i][1][a]);
else {
f[i][1][a] = min(f[i - 1][1][b] + (a != s[i] - '0') * c, f[i][1][a]);
f[i][0][a] = min(f[i - 1][0][b] + (a != s[i] - '0') * c, f[i][0][a]);
}
}
}
}
cout << min(f[n][1][0], f[n][1][1]) << '\n';
E
显然对于一行/列的所有操作,只有最后一次操作生效,但是对于一列染色,有可能中间的若干行会被行染色覆盖,所以可以这么计数:
首先默认没有行染色覆盖当前列,然后减去所有时间戳大于当前列的行的个数,这些行会覆盖当前列的一些方块,同理,还需要为每一个行染色加上时间戳小于当前行的列的个数,一开始全是 0 可以看作 \(n\) 次行染色,时间戳为 \(0\)。
for(int i = 1; i <= n; i ++)
r[i] = {0, 0};
for(int i = 1, op, x, cl; i <= q; i ++) {
cin >> op >> x >> cl;
if(op == 1) r[x] = {cl, i};
else c[x] = {cl, i};
}
for(int i = 1; i <= n; i ++)
cntr[r[i].y] ++;
for(int i = q; i >= 0; i --) cntr[i] += cntr[i + 1];
for(int i = 1; i <= m; i ++) {
cnt[c[i].x] += n;
cnt[c[i].x] -= cntr[c[i].y];
}
for(int i = 1; i <= m; i ++)
cntc[c[i].y] ++;
for(int i = 1; i <= q; i ++) cntc[i] += cntc[i - 1];
for(int i = 1; i <= n; i ++)
cnt[r[i].x] += cntc[r[i].y];
F
观察到答案具有二分性,考虑二分答案,然后贪心选择最靠前的匹配字符,正确性显然,难点在于怎么找到某一个位置往后的第 \(k\) 个相同字符的位置,和 B 一样,分讨就可以了,只是有点恶心。
时间复杂度:\(O(n\log V\log n)\)。
// Problem: F - SSttrriinngg in StringString
// Contest: AtCoder - UNIQUE VISION Programming Contest 2024 Spring(AtCoder Beginner Contest 346)
// Author: Moyou
// Copyright (c) 2024 Moyou All rights reserved.
// Date: 2024-03-23 20:54:15
#include <algorithm>
#include <iostream>
#include <queue>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int n, ls, lt, cnt[N][30], pos[N];
string s, t;
vector<int> p[30];
int Next(int i, int c, int k) {
if(!k) return i;
int r = (i - 1) % ls + 1, d = (i - 1) / ls;
if(cnt[r + 1][c] >= k) return i - r + p[c][pos[r] + k];
k -= cnt[r + 1][c];
i = (i + ls - 1) / ls * ls;
if(k % cnt[1][c] == 0) return i + p[c].back() + (k / cnt[1][c] - 1) * ls;
return p[c][k % cnt[1][c] - 1] + i + (k / cnt[1][c]) * ls;
}
bool check(int m) {
for(int i = 1, now = 0; i < t.size(); i ++) {
if(cnt[1][t[i] - 'a'] == 0) return 0;
int r = (now - 1) % ls + 1;
if(i == 1) {
now = p[t[i] - 'a'][0];
}
else {
if(r >= p[t[i] - 'a'].back()) now += ls - r + p[t[i] - 'a'][0];
else now += *upper_bound(p[t[i] - 'a'].begin(), p[t[i] - 'a'].end(), r) - r;
}
now = Next(now, t[i] - 'a', m - 1);
if(now > n * ls) return 0;
}
return 1;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> s >> t;
ls = s.size(), lt = t.size();
s = " " + s, t = " " + t;
for(int i = ls; i; i --) {
for(int j = 0; j < 26; j ++) cnt[i][j] = cnt[i + 1][j];
cnt[i][s[i] - 'a'] ++;
}
for(int i = 1; i <= ls; i ++) {
p[s[i] - 'a'].push_back(i);
pos[i] = p[s[i] - 'a'].size() - 1;
}
int l = 1, r = n * ls / lt + 1, ans = 0;
while(l <= r) {
int mid = l + r >> 1;
if(check(mid)) l = mid + 1, ans = mid;
else r = mid - 1;
}
cout << ans << '\n';
return 0;
}
G
不难想到固定那个出现一次的位置,然后计算有多少区间覆盖了它,找前驱后继后,发现需要统计 \(\exists l_i\le L\le i \le R\le r_i\) 的 \((L, R)\) 个数,而且同一个对只计算一次,所以可以联想到扫描线求矩形并。
// Problem: G - Alone
// Contest: AtCoder - UNIQUE VISION Programming Contest 2024 Spring(AtCoder Beginner Contest 346)
// Author: Moyou
// Copyright (c) 2024 Moyou All rights reserved.
// Date: 2024-03-23 23:52:18
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 4e5 + 10;
int n, a[N], idx;
vector<int> c[N];
struct owo {
int l, r, op;
} ;
vector<owo> p[N];
struct qwq {
int l, r, dat, tag, cnt;
} tr[N << 2];
qwq up(qwq u, qwq l, qwq r) {
if(l.dat < r.dat) u.dat = l.dat, u.cnt = l.cnt;
else if(l.dat > r.dat) u.dat = r.dat, u.cnt = r.cnt;
else u.dat = l.dat, u.cnt = l.cnt + r.cnt;
return u;
}
void align(int u, int v) {tr[u].dat += v; tr[u].tag += v;}
void down(int u) {
if(tr[u].tag)
align(u << 1, tr[u].tag), align(u << 1 | 1, tr[u].tag), tr[u].tag = 0;
}
void build(int u, int l, int r) {
int mid = l + r >> 1;
tr[u] = {l, r, 0, 0, 0};
if(l == r) return tr[u].cnt = 1, void();
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r), tr[u] = up(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
void update(int u, int ql, int qr, int v) {
int l = tr[u].l, r = tr[u].r, mid = l + r >> 1;
if(ql <= l && qr >= r) return align(u, v), void();
down(u);
if(ql <= mid) update(u << 1, ql, qr, v);
if(qr > mid) update(u << 1 | 1, ql, qr, v);
tr[u] = up(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i], c[a[i]].push_back(i);
for(int i = 1; i <= n; i ++)
if(c[i].size())
for(int j = 0; j < c[i].size(); j ++) {
int l = j ? c[i][j - 1] + 1 : 1, r = (j + 1 == c[i].size() ? n : c[i][j + 1] - 1), k = c[i][j];
if(l <= k && k <= r)
p[l].emplace_back(k, r, 1), p[k + 1].emplace_back(k, r, -1);
}
build(1, 1, n);
long long ans = 0;
for(int i = 1; i <= n; i ++) {
for(auto [l, r, op] : p[i])
update(1, l, r, op);
ans += n - (!tr[1].dat ? tr[1].cnt : 0);
}
cout << ans << '\n';
return 0;
}
总结
D 看到了一眼秒了,没有想好细节,调了 10min,由于这些时间的浪费,没有场切 F,更没有仔细想 G,总之切完 ABC 之后要稳心态,不要急。