The 2024 ICPC Asia Hangzhou Regional Contest

外榜 https://board.xcpcio.com/icpc/49th/hangzhou

铜线: 4题739 ~ 5题726

银线: 5题723-370 ~ 6题552

金线: 6题495-470 7题+

难度(个人感觉):

纯签: AK

半签: H

Easy: EM

Mid:

Hard: BF 主要是本蒟蒻不会SCC(bushi

别的难以触碰

A

题意

三个字符串\(s_1 s_2 s_3\),重新映射26个小写字母,问能不能存在映射方案使得\(s_1=s_2\)且\(s_1 \neq s_3\)。

思路

先根据长度特判一下,如果全等长,再把\(s_2\)每个字母全变成\(s_1\)的,直接判断\(s_1s_3\)是不是相等就好了。

代码

struct DSU {
std::vector<int> fa, siz;
DSU(int n) : fa(n + 1), siz(n + 1, 1) {
std::iota(all(fa), 0);
}
int find(int x) {
while (x != fa[x]) {
x = fa[x] = fa[fa[x]];
}
return x;
}
bool merge(int x, int y) {
x = find(x); y = find(y);
if (x == y) return 0;
siz[x] += siz[y];
fa[y] = x;
return 1;
}
};
void solve() {
string a, b, c;
cin >> a >> b >> c;
int x = a.size(), y = b.size(), z = c.size();
if (x != y) {
cout << "NO\n";
return;
}
if (x != z) {
cout << "YES\n";
return;
}
DSU dsu(200);
for (int i = 0; i < x; i++) {
dsu.merge(a[i], b[i]);
}
for (int i = 0; i < x; i++) {
if (dsu.find(a[i]) != dsu.find(c[i])) {
cout << "YES\n";
return;
}
}
cout << "NO\n";
}

K

题意

一个\(n \times m\)排列\(p\),一个\(n \times m\)网格 第\(i\)行\(j\)列编号\((i-1)\times m+j\)。有最多\(k\)次修改排列的机会,每次可以交换两个元素。

第\(x\)次操作标记\(p_x\)位置,求最小的\(x\),使得\(x\)次操作后存在某一行全部被标记。

思路

先把排列按行分开,考虑每一行,求出该行被完全标记所需的最小操作次数。从后往前枚举,要想减少答案,最后一个肯定要往前挪,答案就是下标第\(k+1\)大,取答案最小的那一行和\(m\)的较大值。

代码

void solve() {
int n, m, k;
cin >> n >> m >> k;
vector<int> p(n * m);
for (int i = 0; i < n * m; i++) {
cin >> p[i];
}
int ans = n * m;
std::vector<int> cnt(n);
for (int i = n * m - 1; i >= 0; i--) {
int t = (p[i] - 1) / m;
cnt[t]++;
if (cnt[t] <= k + 1) {
ans = std::min(ans, i + 1);
}
}
cout << std::max(ans, m) << "\n";
}

H

题意

一棵有根树,将每个非根节点标记成轻或重。

要求每个非叶子节点的子节点中,子树大小最大的是重,别的都是轻。这样每个节点都属于一条重链(\(x_1, x_2,...x_k\),满足\(x_1\)是轻或者根,\(x_i\)是\(x_{i-1}\)的子节点且为重,\(x_k\)是叶子)。

输入\(k\)条重链,构造树的结构,输出每个节点的父亲,或者说不存在方案。

思路

普遍想法,把所有链都挂到最长链的头,这样最长链的头当根,判断合不合法就行,只要最长唯一,就合法,不唯一就把最小的挂上去变成最长。

特判一下特殊情况,比如一条链,最长不唯一且挂上最小也不合法,所有链等长。

代码

void solve() {
int n, k;
cin >> n >> k;
struct node {
int l, r, len;
};
vector<node> a(k);
for (int i = 0; i < k; i++) {
auto &[l, r, len] = a[i];
cin >> l >> r;
len = r - l + 1;
}
std::sort(all(a), [&] (auto x, auto y) {
return x.len > y.len;
});
if (k == 1) {
for (int i = 0; i < n; i++) {
cout << i << " \n"[i == n - 1];
}
return;
}
if (a[0].len == a.back().len) {
cout << "IMPOSSIBLE\n";
return;
}
vector<int> fa(n + 1);
for (int i = 0; i < k; i++) {
if (i) {
fa[a[i].l] = a[0].l;
}
for (int j = a[i].l + 1; j <= a[i].r; j++) {
fa[j] = j - 1;
}
}
if (a[0].len == a[1].len) {
if (a[0].len - 2 < a.back().len) {
cout << "IMPOSSIBLE\n";
return;
}
fa[a.back().l] = a[0].l + 1;
}
for (int i = 1; i <= n; i++) {
cout << fa[i] << " \n"[i == n];
}
}

E

题意

大楼里一座电梯,最初在第\(F\)层,每次最多只能载一人。有\(n\)个人,第\(i\)个人想从第\(l_i\)层乘坐到第\(r_i\)层,把\(n\)个人运送完,求电梯的最小上升层数和载人顺序。

思路

  • 究极贪心

答案肯定不小于所有人的区间之和,再加上无用功(不载人往上走)。

当前所在位置在某个人的区间之内,去载他,没有就直接往上走,直到最高点。这时再往下,遇到r就载。

本着能载就载,不能就走的原则,到达最高点,此后不会再有无用功。

因此,按照\(r\)排序,先找到达最高点的序列,剩下的依次即可。

代码

void solve() {
int n, F;
cin >> n >> F;
struct node {
int l, r, id;
};
vector<node> a(n);
ll ans = 0;
for (int i = 0; i < n; i++) {
auto &[l, r, id] = a[i];
cin >> l >> r;
id = i + 1;
if (r >= F) {
F = std::min(F, l);
}
ans += r - l;
}
std::sort(all(a), [&] (auto a, auto b) {
return a.r == b.r ? a.l < b.l : a.r > b.r;
});
std::deque<int> q;
vector<bool> vis(n + 1);
int now = a[0].r;
int add = 2e9;
for (int i = 0; i < n; i++) {
if (a[i].l >= F) {
add = std::min(add, a[i].l - F);
if (now > a[i].r) {
ans += now - a[i].r;
}
if (a[i].l < now) {
q.push_front(a[i].id);
vis[a[i].id] = 1;
}
now = std::min(now, a[i].l);
}
}
for (int i = 0; i < n; i++) {
if (!vis[a[i].id]) {
q.push_back(a[i].id);
}
}
if (add != 2e9) ans += add;
cout << ans << '\n';
while (!q.empty()) {
cout << q.front() << " ";
q.pop_front();
}
cout << '\n';
}

M

题意

一个序列\(b\)和一个整数\(k \le 10^9\),求\(\sum_{x=1}^{k} [b_1+x,b_2+x,...b_n+x为可整除序列] \times x\)。

可整除区间: 存在整数\(d \in [l, r]\)满足\(b_l,b_{l+1},...b_r\)内的所有数都能被\(b_d\)整除。

可整除序列: 对于任意区间,都是可整除区间。

思路

首先要知道,原数组gcd,可以整除差分数组gcd,也一定是最小值的因子,即\(gcd_原\)=std::gcd(min,\(gcd_{差分}\))。

区间越长,gcd的限制一定越多,也就越小。对于大区间[l,r]满足的\(x\),小区间[a,b]也满足。

所以先预处理出整个区间满足条件的\(x\)(即差分gcd的因子并且\(1 \le x-min \le k\))存下来,答案一定是这个集合的子集。

然后去枚举\(i\),判断以\(i\)位置为最小值的最大区间(单调栈)符不符合。

应该就是个笛卡尔树。本蒟蒻不会

代码

constexpr int N = 5e4 + 5;
constexpr int M = 18; int Log[N], power[M];
int init = []() {
Log[2] = 1;
for (int i = 3; i < N; i++) {
Log[i] = Log[i >> 1] + 1;
}
power[0] = 1;
for (int i = 1; i < M; i++) {
power[i] = power[i - 1] << 1;
}
return 0;
}(); template<typename Info>
struct SparseTable {
Info info[M][N]; void assign(int n) {
for (int i = 0; i < M; i++) {
for (int j = 0; j <= n; j++) {
info[i][j] = Info();
}
}
}
void build(auto &a) {
int n = a.size() - 1;
assign(n);
for (int i = 0; i <= n; i++) {
info[0][i] = Info(a[i]);
}
for (int i = 1; i < M; i++) {
for (int j = 0; j + power[i] - 1 <= n; j++) {
info[i][j] = info[i - 1][j] + info[i - 1][j + power[i - 1]];
}
}
}
Info query(int l, int r) {
int log = Log[r - l + 1];
return info[log][l] + info[log][r - power[log] + 1];
}
};
struct Info {
int gcd;
Info(int gcd = 0) : gcd(gcd) {}
};
Info operator + (const Info &l, const Info &r) {
Info res;
res.gcd = std::gcd(l.gcd, r.gcd);
return res;
}
SparseTable<Info> ST; void solve() {
int n, k;
cin >> n >> k;
vector<int> b(n + 1);
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
int min = *std::min_element(b.begin() + 1, b.end());
int max = *std::max_element(all(b));
if (max == min) {
cout << k << " " << 1LL * (1 + k) * k / 2 << "\n";
return;
} vector<int> diff(n + 1);
for (int i = 1; i <= n; i++) {
diff[i] = std::abs(b[i] - b[i - 1]);
}
ST.build(diff);
vector<int> x;
int g0 = ST.query(2, n).gcd;
for (int i = 1; i * i <= g0; i++) {
if (g0 % i == 0) {
if (i > min && i - min <= k) {
x.push_back(i - min);
}
if (i * i < g0 && g0 / i > min && g0 / i - min <= k) {
x.push_back(g0 / i - min);
}
}
} std::vector<int> stk(n + 1);
std::vector<int> l(n + 1), r(n + 1, n + 1);
int top = 0;
for (int i = 1; i <= n; i++) {
while (top && b[stk[top]] >= b[i]) {
int j = stk[top--];
r[j] = i;
if (top) {
l[j] = stk[top];
}
}
stk[++top] = i;
}
while (top) {
int j = stk[top--];
if (top) {
l[j] = stk[top];
}
}
for (int i = n; i >= 1; i--) {
if (r[i] <= n && b[i] == b[r[i]]) {
r[i] = r[r[i]];
}
} vector<bool> del((int)x.size());
for (int i = 1; i <= n; i++) {
int L = l[i] + 1, R = r[i] - 1;
if (L >= R) continue;
int g = ST.query(L + 1, R).gcd;
for (int j = 0; j < x.size(); j++) {
if (std::gcd(b[i] + x[j], g) % (b[i] + x[j])) {
del[j] = 1;
}
}
} ll ans = 0, cnt = 0;
for (int i = 0; i < x.size(); i++) {
if (!del[i]) {
ans += x[i];
cnt++;
}
}
cout << cnt << " " << ans << '\n';
}

B

题意

子序列\(a_{p_1},a_{p_2},...a_{p_k}\)的等级为\(k\)个数的按位与。

\(q\)次查询,

1 l r x: 区间内每个数按位与上x, \(a_l,a_{l+1},...a_r\)变成\(a_l\)&x, \(a_{l+1}\)&x,...\(a_r\)&x。

2 s x: \(a_s\) = x

3 l r (l < r): 求区间内选r-l个数组成的序列的最大等级。

思路

  • 线段树二分

修改操作线段树懒标记修改即可。

对于查询,区间内删除只有一个0别的都是1的最高数位所在的数。枚举每个数位,二分删除的位置,时间复杂度\(O(q\times logn \times loga_i)\)不能接受。

看官方题解,难点就在于状态压缩,用一个数表示当前区间内某个数位恰好一个0。

代码

template<class Info, class Tag>
class LazySegmentTree {
private:
int n;
std::vector<Info> info;
std::vector<Tag> tag; void apply(int o, const Tag &t) { tag[o].apply(t), info[o].apply(t); } void push(int o) { apply(o << 1, tag[o]), apply(o << 1 | 1, tag[o]), tag[o] = Tag(); } void pull(int o) { info[o] = info[o << 1] + info[o << 1 | 1]; } public:
LazySegmentTree(int n) : n(n), info(n << 2, Info()), tag(n << 2, Tag()) {} void build(int o, int l, int r, const std::vector<Info> &a) {
if (l == r) {
info[o] = a[l];
return;
}
int mid = l + r >> 1;
build(o << 1, l, mid, a);
build(o << 1 | 1, mid + 1, r, a);
pull(o);
}
void build(const std::vector<Info> &a) { build(1, 1, n, a); }
void modify(int o, int l, int r, int x, int y, const Tag &tag) {
if (x <= l && r <= y) {
apply(o, tag);
return;
}
push(o);
int mid = l + r >> 1;
if (x <= mid) modify(o << 1, l, mid, x, y, tag);
if (y > mid) modify(o << 1 | 1, mid + 1, r, x, y, tag);
pull(o);
}
void modify(int o, int l, int r, int p, const Tag &tag) {
if (l == r) {
info[o] = Info(tag.mask, (~tag.mask), 1);
return;
}
push(o);
int mid = l + r >> 1;
if (p <= mid) modify(o << 1, l, mid, p, tag);
else modify(o << 1 | 1, mid + 1, r, p, tag);
pull(o);
}
void modify(int p, const Tag &tag) { modify(1, 1, n, p, tag); }
void modify(int l, int r, const Tag &tag) { modify(1, 1, n, l, r, tag); } Info query(int o, int l, int r, int x, int y) {
if (x <= l && r <= y) {
return info[o];
}
push(o);
int mid = l + r >> 1;
if (y <= mid) return query(o << 1, l, mid, x, y);
if (x > mid) return query(o << 1 | 1, mid + 1, r, x, y);
return query(o << 1, l, mid, x, y) + query(o << 1 | 1, mid + 1, r, x, y);
}
Info query(int p) { return query(1, 1, n, p, p); }
Info query(int l, int r) { return query(1, 1, n, l, r); }
int get(int o, int l, int r, int p, int k) {
if ((info[o].sum >> k & 1)) {
return 0;
}
if (l == r) {
return l;
}
push(o);
int mid = l + r >> 1;
if (p > mid) {
return get(o << 1 | 1, mid + 1, r, p, k);
}
int L = get(o << 1, l, mid, p, k);
return L ? L : get(o << 1 | 1, mid + 1, r, p, k);
}
int get(int p, int k) { return get(1, 1, n, p, k); }
}; struct Tag {
bool need;
ll mask; Tag(bool need = 0, ll mask = ~0LL) : need(need), mask(mask) {}
void apply(const Tag &t) {
if (t.need) {
mask &= t.mask;
need = 1;
}
}
};
struct Info {
long long sum, flag;
int len;
Info(long long sum = ~0LL, long long flag = 0, int len = 1) : sum(sum), flag(flag), len(len) {}
void apply(const Tag &t) {
if (t.need) {
sum &= t.mask;
if (len == 1) {
flag = (flag & t.mask) | (~t.mask);
} else {
flag &= t.mask;
}
}
}
}; Info operator+(const Info &L, const Info &R) {
Info res;
res.sum = L.sum & R.sum;
res.flag = (L.flag & R.sum) | (R.flag & L.sum);
res.len = L.len + R.len;
return res;
} void solve() {
int n, q;
cin >> n >> q;
vector<ll> a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
vector<Info> aa(n + 1);
for (int i = 1; i <= n; i++) {
aa[i] = Info(a[i], (~a[i]), 1);
}
LazySegmentTree<Info, Tag> seg(n);
seg.build(aa); while (q--) {
int op;
cin >> op;
if (op == 1) {
int l, r;
ll x;
cin >> l >> r >> x;
seg.modify(l, r, Tag(1, x));
} else if (op == 2) {
int s;
ll x;
cin >> s >> x;
seg.modify(s, Tag(0, x));
} else {
int l, r;
cin >> l >> r;
if (l == r) {
cout << 0 << "\n";
continue;
}
auto [res, flag, len] = seg.query(l, r);
if (!flag) {
cout << res << "\n";
continue;
}
for (int i = 63; i >= 0; i--) {
if (flag >> i & 1) {
int p = seg.get(l, i);
ll ans = (~0LL);
if (p > l) {
ans &= seg.query(l, p - 1).sum;
}
if (p < r) {
ans &= seg.query(p + 1, r).sum;
}
cout << ans << '\n';
break;
}
}
}
}
}

2024 ICPC杭州的更多相关文章

  1. HDU 4741 Save Labman No.004 2013 ACM/ICPC 杭州网络赛

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4741 题意:给你两条异面直线,然你求着两条直线的最短距离,并求出这条中垂线与两直线的交点. 需要注意的是 ...

  2. Substrings 第37届ACM/ICPC 杭州赛区现场赛C题(hdu 4455)

    http://acm.hdu.edu.cn/showproblem.php?pid=4455 https://icpcarchive.ecs.baylor.edu/index.php?option=c ...

  3. 2013 ACM/ICPC 杭州网络赛C题

    题意:驴和老虎,在一个矩阵的两个格子里,有各自的起始方向.两者以相同的速度向前移动,前方不能走时驴总是向右,老虎总是向左.他们不能超出矩阵边界也不能走自己走过的格子(但可以走对方走过的格子).如果不能 ...

  4. hdu 4461 第37届ACM/ICPC杭州赛区I题

    题意:给两个人一些棋子,每个棋子有其对应的power,若b没有或者c没有,或者二者都没有,那么他的total power就会减1,total power最少是1,求最后谁能赢 如果b或c出现的话,fl ...

  5. hdu 4460 第37届ACM/ICPC杭州赛区H题 STL+bfs

    题意:一些小伙伴之间有朋友关系,比如a和b是朋友,b和c是朋友,a和c不是朋友,则a和c之间存在朋友链,且大小为2,给出一些关系,求出这些关系中最大的链是多少? 求最短路的最大距离 #include& ...

  6. hdu 4462 第37届ACM/ICPC 杭州赛区 J题

    题意:有一块n*n的田,田上有一些点可以放置稻草人,再给出一些稻草人,每个稻草人有其覆盖的距离ri,距离为曼哈顿距离,求要覆盖到所有的格子最少需要放置几个稻草人 由于稻草人数量很少,所以状态压缩枚举, ...

  7. hdu 4463 第37届ACM/ICPC杭州赛区K题 最小生成树

    题意:给坐标系上的一些点,其中有两个点已经连了一条边,求最小生成树的值 将已连接的两点权值置为0,这样一定能加入最小生成树里 最后的结果加上这两点的距离即为所求 #include<cstdio& ...

  8. 2013 ACM/ICPC 亚洲区 杭州站

    题目链接  2013杭州区域赛 Problem A Problem B 这题我用的是SPFA+ mask dp 首先跑5次SPFA: 1次是求出每个起点和其他所有点的最短距离 4次是求出每个输入的点和 ...

  9. 【转】lonekight@xmu·ACM/ICPC 回忆录

    转自:http://hi.baidu.com/ordeder/item/2a342a7fe7cb9e336dc37c89 2009年09月06日 星期日 21:55 初识ACM最早听说ACM/ICPC ...

  10. 【转】ACM/ICPC生涯总结暨退役宣言—alpc55

    转自:http://hi.baidu.com/accplaystation/item/ca4c2ec565fa0b7fced4f811 ACM/ICPC生涯总结暨退役宣言—alpc55 前言 早就该写 ...

随机推荐

  1. 把 PySide6 移植到安卓上去!

    官方教程在此:https://www.qt.io/blog/taking-qt-for-python-to-android 寥寥几句,其实不少坑.凭回忆写的,可能不是很全(无招胜有招) 仅支持 Lin ...

  2. java代码运行出现DENIED Redis is running in protected mode because protected mode is enabled 问题解决

    这个错误是因为开启了保护模式,导致出错.所以需要关闭redis的保护模式. 编辑redis的redis.config 注释 bind 127.0.0.1  .修改protected-mode 为 no ...

  3. 打工人必备!2025年最强任务管理软件Top5测评推荐

    前言:谁偷走了我们的时间? 你是否也有这样的经历: 今天本来打算写一份PPT,结果临时被拉去开会,文档又拖到了明天: 任务堆成山,却总忘记哪个最重要: 同事催你对接.老板问你进度,你满脑子问号-- 这 ...

  4. 鸿蒙Next仓颉语言开发实战教程:懒加载

    今天要分享的是仓颉开发语言中的懒加载. 先和初学者朋友们解释一下什么是懒加载.懒加载在代码中叫做LazyForEach,看到名字你一定能猜到它和ForEach的功能类似.只不过和ForEach的一次性 ...

  5. 【附源码】用Spring AI通杀所有MCP客户端,简直离谱!

    在上一章节中,我们讲解了MCP服务,并以Spring AI作为客户端和服务端进行示例说明.然而,当前市面上已经存在众多成熟的MCP客户端和服务端实现.那么,Spring AI在这些现有方案中的适配程度 ...

  6. 深度学习模型在C++平台的部署

    一.概述   深度学习模型能够在各种生产场景中发挥重要的作用,而深度学习模型往往在Python环境下完成训练,因而训练好的模型如何在生产环境下实现稳定可靠的部署,便是一个重要内容.C++开发平台广泛存 ...

  7. AI领域又新增协议: AG-UI

    随着AI的快速发展正在重塑技术生态,协议的演进速度尤为迅猛.一个令人头疼的问题浮现了:不同的AI智能体和前端应用之间就像说着不同语言的人,无法顺畅交流.开发者们需要为每个智能体单独编写接口,维护成本高 ...

  8. java 打印日历

    简介 示例代码 Result Mon Tue Wed Thu Fri Sat Sun 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...

  9. 什么是ETL?什么是ELT?怎么区分它们使用场景

    ELT和ETL这两种模式从字面上来看就是一个顺序颠倒的问题,每个单词拆开来看其实都是一样的.E代表的是Extract(抽取),也就是从源端拉取数据:T代表的是Transform(转换),对一些结构化或 ...

  10. 如何让java中的注释代码执行?

    直接上代码: @Test public void testUnicode() { String a = "Hello"; // \u000d a="world" ...