The 2022 ICPC Asia-East Continent Final Contest (EC-Final 2022)【杂题】
没做完。
A. Coloring
有
可以进行任意操作:选择一个
你的收益是最后
很炫酷的题。
整个图的结构是基环树。不妨假设
子树里如果
然后考虑环上,记
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <LL, LL> pi;
#define fi first
#define se second
constexpr int N = 5e3 + 5;
constexpr LL inf = 1e18;
bool Mbe;
int n, s, a[N], vis[N], cir[N], len;
vector <int> e[N];
LL w[N], p[N];
LL f[N][N], g[N][N];
void chkmx(LL &x, LL y) {
x = x > y ? x : y;
}
void dfs(int u) {
for (auto v : e[u]) {
if (vis[v]) continue;
dfs(v);
for (int i = 0; i <= n; i++) {
f[u][i] += g[v][i];
}
}
for (int i = 0; i <= n; i++) {
f[u][i] += w[u] * (i & 1) - p[u] * i;
g[u][i] = f[u][i];
if (i) {
chkmx(g[u][i], g[u][i - 1]);
}
}
}
void solve() {
cin >> n >> s;
for (int i = 1; i <= n; i++)
cin >> w[i];
for (int i = 1; i <= n; i++)
cin >> p[i];
for (int i = 1; i <= n; i++) {
cin >> a[i];
e[a[i]].emplace_back(i);
}
int u = a[s];
while (!vis[u]) {
vis[u] = 1;
cir[++len] = u;
u = a[u];
}
if (!vis[s]) {
dfs(s);
cout << max(f[s][1], f[s][2]) + p[s] << "\n";
} else if (len == 2) {
dfs(s);
dfs(a[s]);
cout << max(f[s][1], max(f[s][2], f[s][1] + f[a[s]][1])) + p[s] << "\n";
} else {
reverse(cir + 1, cir + len + 1);
for (int i = 1; i <= len; i++) {
dfs(cir[i]);
}
assert(cir[1] == s);
f[s][0] = -inf;
LL ans = -inf;
static LL dp[3];
for (int i = 0; i <= n; i++) {
dp[0] = dp[1] = dp[2] = 0;
for (int j = 1; j <= len; j++) {
for (int k = 0; k < 3; k++) {
if (i + k <= n) dp[k] += f[cir[j]][i + k];
else dp[k] = -inf;
}
chkmx(dp[1], dp[2]);
chkmx(dp[0], dp[1]);
}
chkmx(ans, dp[0]);
}
cout << ans + p[s] << "\n";
}
}
bool Med;
int main() {
// fprintf(stderr, "%.9lf\n", 1.0 * (&Mbe - &Med) / 1048576.0);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t; t = 1;
while (t--) solve();
// cerr << 1e3 * clock() / CLOCKS_PER_SEC << " ms\n";
return 0;
}
B. Binary String
给定一个
很炫酷的题。
以下的 “段” 均指长度大于
- 对于一个
段,如果其左边不和某个 段相邻,变换后会整体往左移动一位。 - 对于一个
段,如果其右边不和某个 段相邻,变换后会整体往右移动一位。 - 如果一个
段接在一个 段后面,那么相当于两个段相撞,变换后长度都会减 。
这个过程会一直执行直到不存在
考虑怎么求出最后这个串。不妨假设
注意到这个过程和括号匹配很像阿,考虑如果不是环而是序列的话,实际上我们可以直接模拟一遍求出每个
那考虑规避掉这种情况。根据 Raney 引理,我们总是能找到一个起点位置,使得每个前缀中
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <LL, LL> pi;
#define fi first
#define se second
#define all(x) x.begin(), x.end()
#define debug cerr << "[" << __LINE__ << "] "
constexpr int N = 1e7 + 5, mod = 998244353;
constexpr LL inf = 1e18;
bool Mbe;
string s;
int n, kmp[N], c[N], nc[N];
void solve() {
cin >> s;
int c0 = count(all(s), '0'), c1 = count(all(s), '1');
if (c0 < c1) {
string str = "";
for (auto i : s) str += i ^ 1;
reverse(all(str));
swap(s, str);
}
n = s.size();
s = ' ' + s;
for (int i = 1; i <= n; i++) {
if (s[i] == '0') {
string str = " ";
for (int j = 1; j <= n; j++) str += s[(i + j - 2) % n + 1];
swap(s, str);
break;
}
}
int tot = 0;
for (int i = 1; i <= n; ) {
if (s[i] == '0') {
int j = i + 1;
while (j <= n && s[j] == '1') j++;
c[++tot] = j - i - 1;
i = j;
}
}
int sum = 0, mn = 0, pos = 0;
for (int i = 1; i <= tot; i++) {
sum += c[i] - 1;
if (sum < mn) {
mn = sum;
pos = i;
}
}
for (int i = 1; i <= tot; i++) {
nc[i] = c[i];
}
for (int i = 1; i <= tot; i++) {
c[i] = nc[(i + pos - 1) % tot + 1];
}
int ans = 0;
for (int i = 1; i <= tot; i++) {
if (c[i] <= 1) continue;
int j = i, v = 0, sum = 0;
while (true) {
v += c[j] - 1;
sum++;
if (v <= 0) break;
j++;
}
ans = max(ans, sum - 1);
for (int k = i; k <= j; k++) {
c[k] = 1;
}
}
string str = " ";
for (int i = 1; i <= tot; i++) {
str += '0';
if (c[i]) str += '1';
}
int j = 0;
for (int i = 2; i <= n; i++) {
while (j && str[i] != str[j + 1]) j = kmp[j];
if (str[i] == str[j + 1]) j++;
kmp[i] = j;
}
int t = n - kmp[n];
if (n % t == 0) ans += t;
else ans += n;
cout << ans << "\n";
}
bool Med;
int main() {
// fprintf(stderr, "%.9lf\n", 1.0 * (&Mbe - &Med) / 1048576.0);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t; cin >> t;
while (t--) solve();
// cerr << 1e3 * clock() / CLOCKS_PER_SEC << " ms\n";
return 0;
}
C. Best Carry Player 2
给定
考虑从高位往低位数位 DP,设
注意一些细节:由于
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <LL, LL> pi;
typedef tuple <int, int, int> ti3;
#define fi first
#define se second
constexpr int N = 1e6 + 5, M = 20;
constexpr LL inf = 1e18;
bool Mbe;
LL p[M];
void init() {
p[0] = 1;
for (int i = 1; i <= 18; i++) p[i] = p[i - 1] * 10;
}
LL x; int k;
LL f[M][M][2];
void chkmn(LL &x, LL y) {
x = min(x, y);
}
void mian() {
cin >> x >> k;
if (k == 0) {
for (int i = 0; i <= 17; i++) {
int cur = x / p[i] % 10;
if (cur < 9) {
cout << p[i] << "\n";
return;
}
}
cout << p[18] << "\n";
return;
}
int cnt = 0;
while (x % 10 == 0) cnt++, x /= 10;
for (int i = 0; i <= 18; i++) for (int j = 0; j <= k; j++) f[i][j][0] = f[i][j][1] = inf;
f[0][0][0] = 0;
if (x % 10) f[0][1][1] = 10 - x % 10;
for (int i = 1; i <= 18; i++) {
int cur = x / p[i] % 10;
for (int j = 0; j <= k; j++) {
chkmn(f[i][j][0], f[i - 1][j][0]);
if (cur < 9) chkmn(f[i][j][0], f[i - 1][j][1]);
if (j) {
if (cur) chkmn(f[i][j][1], f[i - 1][j - 1][0] + (10 - cur) * p[i]);
chkmn(f[i][j][1], f[i - 1][j - 1][1] + (9 - cur) * p[i]);
}
}
}
cout << f[18][k][0];
for (int i = 1; i <= cnt; i++) cout << "0";
cout << "\n";
}
bool Med;
int main() {
// fprintf(stderr, "%.9lf\n", 1.0 * (&Mbe - &Med) / 1048576.0);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
init();
int t; cin >> t;
while (t--) mian();
// cerr << 1e3 * clock() / CLOCKS_PER_SEC << " ms\n";
return 0;
}
G. Rectangle
给定
求方案数对
很炫酷的题。
根据直线的情况分类,有三条横线,一横两竖,以及对称情况。
对于三条横线,只和所有区间的
对于一横两竖,考虑对横线做扫描线,变成了你要支持删除一个区间,插入一个区间,问用两个点覆盖剩下区间的方案数。假设所有区间为
维护
细节很多,总时间复杂度
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <LL, LL> pi;
#define fi first
#define se second
constexpr int N = 2e5 + 5, T = 1e9, mod = 998244353, inv6 = 166374059;
constexpr LL inf = 1e18;
bool Mbe;
#define x1 elma1
#define y1 elma2
#define x2 elma3
#define y2 elma4
#define m ((l + r) >> 1)
#define lc x << 1
#define rc x << 1 | 1
void chkmx(int &x, int y) { x = x > y ? x : y; }
void chkmn(int &x, int y) { x = x < y ? x : y; }
void add(int &x, LL y) {
x = x + y >= mod ? x + y - mod : x + y;
}
int n, x1[N], y1[N], x2[N], y2[N], yy[N], ans, lp[N], rp[N];
int cx, cy, tx[N], ty[N];
int len[N << 2], mx[N << 2], tag[N << 2], sum[N << 2], lmax[N], rmin[N];
void build(int x, int l, int r) {
sum[x] = mx[x] = tag[x] = 0, len[x] = ty[r + 1] - ty[l];
if (l == r) return;
build(lc, l, m), build(rc, m + 1, r);
}
vector <pi> g1;
vector <pi> g2;
vector <pi> g3;
void push_tag(int x, int v) {
g1.emplace_back(x, mx[x]);
g2.emplace_back(x, tag[x]);
g3.emplace_back(x, sum[x]);
mx[x] = tag[x] = v;
sum[x] = 1LL * len[x] * v % mod;
}
void push_down(int x) {
if (tag[x]) {
g2.emplace_back(x, tag[x]);
push_tag(lc, tag[x]);
push_tag(rc, tag[x]);
tag[x] = 0;
}
}
void push_up(int x) {
g1.emplace_back(x, mx[x]);
g3.emplace_back(x, sum[x]);
mx[x] = mx[rc];
sum[x] = (sum[lc] + sum[rc]) % mod;
}
void cov(int x, int l, int r, int ql, int qr, int v) {
if (ql <= l && qr >= r) return push_tag(x, v);
push_down(x);
if (ql <= m) cov(lc, l, m, ql, qr, v);
if (qr > m) cov(rc, m + 1, r, ql, qr, v);
push_up(x);
}
int qry(int x, int l, int r, int ql, int qr) {
if (ql <= l && qr >= r) return sum[x];
push_down(x);
int res = 0;
if (ql <= m) add(res, qry(lc, l, m, ql, qr));
if (qr > m) add(res, qry(rc, m + 1, r, ql, qr));
return res;
}
int findpos(int x, int l, int r, int v) {
if (l == r) return l;
push_down(x);
if (mx[lc] > v) return findpos(lc, l, m, v);
return findpos(rc, m + 1, r, v);
}
vector <int> buc[N << 2];
void upd(int x, int l, int r, int ql, int qr, int id) {
if (ql <= l && qr >= r) return buc[x].emplace_back(id), void();
if (ql <= m) upd(lc, l, m, ql, qr, id);
if (qr > m) upd(rc, m + 1, r, ql, qr, id);
}
void solve(int x, int l, int r) {
int t1 = g1.size(), t2 = g2.size(), t3 = g3.size();
for (auto id : buc[x]) {
int nr = mx[1] <= y1[id] ? cy : findpos(1, 1, cy, y1[id]) - 1;
if (yy[id] <= nr) cov(1, 1, cy, yy[id], nr, y1[id]);
}
if (l == r) {
if (lmax[l]) {
int L = lower_bound(ty + 1, ty + cy + 1, lmax[l]) - ty;
if (L <= cy) {
int R = mx[1] <= rmin[l] ? cy : findpos(1, 1, cy, rmin[l]) - 1;
if (L <= R) {
int val = qry(1, 1, cy, L, R);
add(ans, 1LL * (1LL * (ty[R + 1] - ty[L]) * (rmin[l] + 1) % mod - val + mod) % mod * (tx[l + 1] - tx[l]) % mod);
}
}
}
} else solve(lc, l, m), solve(rc, m + 1, r);
while (g1.size() > t1) {
auto [x, y] = g1.back();
mx[x] = y;
g1.pop_back();
}
while (g2.size() > t2) {
auto [x, y] = g2.back();
tag[x] = y;
g2.pop_back();
}
while (g3.size() > t3) {
auto [x, y] = g3.back();
sum[x] = y;
g3.pop_back();
}
}
struct P1 {
priority_queue <int> q1, q2;
void insert(int x) {
q1.push(x);
}
void erase(int x) {
q2.push(x);
}
unsigned size() {
return q1.size() - q2.size();
}
bool empty() {
return q1.size() == q2.size();
}
int top() {
while (q2.size() && q2.top() == q1.top()) q1.pop(), q2.pop();
return q1.top();
}
void pop() {
while (q2.size() && q2.top() == q1.top()) q1.pop(), q2.pop();
q1.pop();
}
};
struct P2 {
priority_queue <int, vector <int>, greater <int>> q1, q2;
void insert(int x) {
q1.push(x);
}
void erase(int x) {
q2.push(x);
}
unsigned size() {
return q1.size() - q2.size();
}
bool empty() {
return q1.size() == q2.size();
}
int top() {
while (q2.size() && q2.top() == q1.top()) q1.pop(), q2.pop();
return q1.top();
}
void pop() {
while (q2.size() && q2.top() == q1.top()) q1.pop(), q2.pop();
q1.pop();
}
};
vector <int> ad[N];
int t1[N];
bool t2[N];
void solve() {
for (int i = 1; i <= 2 * n + 5; i++) {
ad[i].clear();
}
for (int i = 1; i <= 8 * n + 10; i++) {
buc[i].clear();
}
cx = cy = 0;
tx[++cx] = 1;
ty[++cy] = 1;
for (int i = 1; i <= n; i++) {
tx[++cx] = x1[i];
if (x2[i] != T) tx[++cx] = x2[i] + 1;
ty[++cy] = y1[i];
if (y2[i] != T) ty[++cy] = y2[i] + 1;
}
for (int i = 1; i <= cx; i++) lp[i] = 0, rp[i] = T + 1;
sort(tx + 1, tx + cx + 1);
sort(ty + 1, ty + cy + 1);
cx = unique(tx + 1, tx + cx + 1) - tx - 1;
cy = unique(ty + 1, ty + cy + 1) - ty - 1;
tx[cx + 1] = T + 1;
ty[cy + 1] = T + 1;
build(1, 1, cy);
for (int i = 1; i <= n; i++) {
int x1 = lower_bound(tx + 1, tx + cx + 1, ::x1[i]) - tx;
int x2 = lower_bound(tx + 1, tx + cx + 1, ::x2[i] + 1) - tx;
chkmx(lp[x2], ::x1[i]);
chkmn(rp[x1 - 1], ::x2[i]);
yy[i] = lower_bound(ty + 1, ty + cy + 1, y2[i] + 1) - ty;
if (x1 > 1) upd(1, 1, cx, 1, x1 - 1, i), ad[1].emplace_back(i), ad[x1].emplace_back(-i);
if (x2 <= cx) upd(1, 1, cx, x2, cx, i), ad[x2].emplace_back(i);
}
P1 nl;
P2 nr;
int L = 1, R = T;
for (int i = 1; i <= cx; i++) {
for (auto j : ad[i]) {
if (j > 0) {
nl.insert(y1[j]);
nr.insert(y2[j]);
} else {
j *= -1;
nl.erase(y1[j]);
nr.erase(y2[j]);
}
}
if (nl.empty()) lmax[i] = 0, add(ans, 1LL * T * (T - 1) / 2 % mod * (tx[i + 1] - tx[i]) % mod);
else {
lmax[i] = nl.top(), rmin[i] = nr.top();
if (lmax[i] <= rmin[i]) {
LL len = rmin[i] - lmax[i] + 1;
add(ans, 1LL * (1LL * T * (T - 1) / 2 - 1LL * (T - len) * (T - len - 1) / 2) % mod * (tx[i + 1] - tx[i]) % mod);
swap(lmax[i], rmin[i]);
lmax[i]++;
rmin[i]--;
}
}
if (lp[i]) {
chkmx(L, lp[i]);
chkmn(R, tx[i] - 1);
}
t1[i] = max(0, min(R, tx[i] - 1) - L + 1);
t2[i] = R >= tx[i];
}
L = 1, R = T;
for (int i = cx; i >= 1; i--) {
if (rp[i] <= T) {
chkmx(L, tx[i + 1]);
chkmn(R, rp[i]);
}
int k1 = max(0, R - max(L, tx[i + 1]) + 1);
bool k2 = L <= tx[i];
int len = tx[i + 1] - tx[i];
add(ans, 1LL * len * t1[i] % mod * k1 % mod);
if (t2[i]) add(ans, 1LL * len * (len - 1) / 2 % mod * k1 % mod);
if (k2) add(ans, 1LL * len * (len - 1) / 2 % mod * t1[i] % mod);
if (t2[i] && k2) add(ans, 1LL * len * (len - 1) % mod * (len - 2) % mod * inv6 % mod);
}
solve(1, 1, cx);
}
void mian() {
ans = 0;
cin >> n;
for (int i = 1; i <= n; i++) cin >> x1[i] >> y1[i] >> x2[i] >> y2[i];
solve();
for (int i = 1; i <= n; i++) swap(x1[i], y1[i]), swap(x2[i], y2[i]);
solve();
cout << ans << "\n";
}
bool Med;
int main() {
// fprintf(stderr, "%.9lf\n", 1.0 * (&Mbe - &Med) / 1048576.0);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1; cin >> t;
while (t--) mian();
// cerr << 1e3 * clock() / CLOCKS_PER_SEC << " ms\n";
return 0;
}
K. Magic
你有一个序列
给定
你可以指定一个执行操作的顺序,最大化
很炫酷的题。
先做一点基本转化:令
换⼀种方式考虑:要选择若干个位置,使得这些位置最后贡献给答案,要求合法且选择的位置数量最多。对于区间
所以
证明:
只要选择独立集后不会成环,那么就能够说明合法性。
如果成环,假设环上选了一个区间的右端点 ,导致它比某个区间 要晚执行。由于 不能被选择,那么下一个环上的点一定是由于选择了 导致的。那么以此类推,在这条链上的区间 一定单调递增,所以不可能成环。因此该限制充分。
于是问题转为二分图最大独立集问题。使用 bitset
优化匈牙利求出最大匹配即可,时间复杂度
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <LL, LL> pi;
#define fi first
#define se second
constexpr int N = 1e4 + 5, M = 20;
bool Mbe;
int n, m, ans;
int q[N], hd, tl;
int l[N], r[N], col[N], mat[N], prex[N], prey[N];
bitset <N> rest, nxt, e[N];
bool bfs(int s, int n) {
int pos = 0;
q[hd = tl = 1] = s;
rest.set();
while (hd <= tl && !pos) {
int u = q[hd++];
nxt = rest;
nxt &= e[u];
for (int i = nxt._Find_first(); i <= n; i = nxt._Find_next(i)) {
if (!mat[i]) {
mat[i] = u;
pos = u;
break;
}
rest[i] = false;
prex[mat[i]] = u;
prey[mat[i]] = i;
q[++tl] = mat[i];
}
}
if (!pos) return false;
for (int i = pos; i != s; i = prex[i]) {
mat[prey[i]] = prex[i];
}
return true;
}
void solve() {
cin >> n;
m = ans = n * 2;
for (int i = 1; i <= n; i++) {
cin >> l[i] >> r[i];
col[l[i]] = 0;
col[r[i]] = 1;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
if (l[i] < l[j] && l[j] < r[i] && r[i] < r[j])
e[l[j]][r[i]] = 1;
}
for (int i = 1; i <= m; i++)
if (col[i] == 0 && bfs(i, m)) ans--;
cout << ans << "\n";
}
bool Med;
int main() {
// fprintf(stderr, "%.9lf\n", 1.0 * (&Mbe - &Med) / 1048576.0);
// freopen("ex_data3.in", "r", stdin);
// freopen("ex_data3.ans", "w", stdout);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
while (t--) solve();
// cerr << 1e3 * clock() / CLOCKS_PER_SEC << " ms\n";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】