2023 ICPC 合肥区域赛题解 更新至 6 题(The 2023 ICPC Asia Hefei Regional Contest )
Preface
只能说阅读理解能力有待提高,
依旧是复习篇,感觉队友当时开出来的
我会在代码一些有必要的地方加上注释,签到题可能一般就不会写了.
以下是代码火车头:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <set>
#include <queue>
#include <map>
#include <unordered_map>
#include <iomanip>
#define endl '\n'
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rep2(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
template<typename T>
void cc(const vector<T> &tem) {
for (const auto &x: tem) cout << x << ' ';
cout << endl;
}
template<typename T>
void cc(const T &a) { cout << a << endl; }
template<typename T1, typename T2>
void cc(const T1 &a, const T2 &b) { cout << a << ' ' << b << endl; }
template<typename T1, typename T2, typename T3>
void cc(const T1 &a, const T2 &b, const T3 &c) { cout << a << ' ' << b << ' ' << c << endl; }
void cc(const string &s) { cout << s << endl; }
void fileRead() {
#ifdef LOCALL
freopen("D:\\AADVISE\\Clioncode\\untitled2\\in.txt", "r", stdin);
freopen("D:\\AADVISE\\Clioncode\\untitled2\\out.txt", "w", stdout);
#endif
}
void kuaidu() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); }
inline int max(int a, int b) {
if (a < b) return b;
return a;
}
inline double max(double a, double b) {
if (a < b) return b;
return a;
}
inline int min(int a, int b) {
if (a < b) return a;
return b;
}
inline double min(double a, double b) {
if (a < b) return a;
return b;
}
void cmax(int &a, const int &b) { if (b > a) a = b; }
void cmin(int &a, const int &b) { if (b < a) a = b; }
void cmin(double &a, const double &b) { if (b < a) a = b; }
void cmax(double &a, const double &b) { if (b > a) a = b; }
using PII = pair<int, int>;
using i128 = __int128;
using vec_int = std::vector<int>;
using vec_char = std::vector<char>;
using vec_double = std::vector<double>;
using vec_int2 = std::vector<std::vector<int> >;
using que_int = std::queue<int>;
Problem B. Queue Sorting
愣是看不懂一点呢,逃了逃了。。。
Problem C. Cyclic Substrings
评价是回文自动机板子题呢。
首先把字符串×2,然后就是查询那些回文字符串,条件是结尾的下标是大于
那么我们首先就当下标大于
//--------------------------------------------------------------------------------
const int N = 6e6 + 10;
const int M = 1e6 + 10;
const int mod = 998244353;
const int INF = 1e16;
int n, m, T;
int ans;
//--------------------------------------------------------------------------------
//struct or namespace:
class PAM {
private:
//TODO 如果不是小写字母就要修改
constexpr static int M = 11;
int fan(char x) { return (x - '0'); }
void erase_son(int x) {
rep(i, 0, M - 1) F[x].son[i] = 0;
}
int getfail(int x, int i) {
while (s[i - F[x].len - 1] != s[i]) x = F[x].fail;
return x;
}
void add(int c, int id) {
int pa = getfail(las, id);
int &x = F[pa].son[c];
if (!x) {
x = ++tot;
erase_son(x);
F[x].fail = F[getfail(F[pa].fail, id)].son[c];
if (F[x].fail == x) F[x].fail = 0;
F[x].len = F[pa].len + 2;
F[x].cnt = F[F[x].fail].cnt + 1;
F[x].siz = 0;
}
if (id > n)
F[x].siz++;
las = x;
return;
}
public:
string s;
int s_len, las, tot;
struct node {
int len;
int fail;
int cnt;
int son[M];
int siz;
};
node F[N];
node &operator [](int x) { return F[x]; }
void add(char c) {
s_len++;
s.push_back(c);
add(fan(c), s_len);
}
void count() {
rep2(i, tot, 2) {
F[F[i].fail].siz += F[i].siz;
if (F[i].len <= n)
ans += F[i].siz * F[i].siz % mod * F[i].len % mod;
ans %= mod;
}
}
void clear() {
F[0].fail = 1;
F[0].len = 0;
F[1].fail = 0;
F[1].len = -1;
F[0].cnt = F[1].cnt = F[0].siz = F[1].siz = 0;
tot = 1, las = 1;
s_len = 0;
s = "$";
erase_son(0), erase_son(1);
}
};
PAM pam;
//--------------------------------------------------------------------------------
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n;
string s;
cin >> s;
s = s + s;
pam.clear();
for (auto &x: s) {
pam.add(x);
}
pam.count();
cc(ans);
}
return 0;
}
Problem E. Matrix Distances
评价是有一些小ex的一道签到。
能够显然的知道
首先将这个数组排序,然后我们考虑假设当前的位置是
//--------------------------------------------------------------------------------
const int N = 1e6 + 10;
const int M = 1e3 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
int A[M][M];
int f[N], g[N], B[N];
map<int,int> mp;
vec_int dp1[N], dp2[N];
//--------------------------------------------------------------------------------
//struct or namespace:
//--------------------------------------------------------------------------------
int dfs(vec_int &A) {
sort(A.begin(), A.end());
int len = A.size();
rep(i, 1, len) B[i] = A[i - 1];
f[len] = B[len], g[1] = B[1];
rep(i, 2, len) g[i] = g[i - 1] + B[i];
rep2(i, len-1, 1) f[i] = f[i + 1] + B[i];
int ans = 0;
rep(i, 1, len) ans += f[i] - g[i] - (len + 1) * B[i] + 2 * i * B[i];
return ans;
}
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n >> m;
int tot = 0;
rep(i, 1, n)
rep(j, 1, m) {
cin >> A[i][j];
if (mp[A[i][j]] == 0) mp[A[i][j]] = ++tot;
int x = mp[A[i][j]];
dp1[x].push_back(i);
dp2[x].push_back(j);
}
int ans = 0;
rep(i, 1, tot) {
ans += dfs(dp1[i]);
ans += dfs(dp2[i]);
}
cc(ans);
}
return 0;
}
Problem F. Colorful Balloons
签签签签签,就不多说了。直接根据题意模拟就好了。
//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
map<string,int> mp;
//--------------------------------------------------------------------------------
//struct or namespace:
//--------------------------------------------------------------------------------
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n;
rep(i, 1, n) {
string s;
cin >> s;
mp[s]++;
}
int fl = 0;
string ans;
for (auto &[s,val]: mp) {
if (n % 2) if (val >= (n + 1) / 2) ans = s, fl = 1;
if (n % 2 == 0) if (val > n / 2) ans = s, fl = 1;
}
if (fl) cout << ans << endl;
else cout << "uh-oh" << endl;
}
return 0;
}
Problem G. Streak Manipulation
首先二分就不说了,直接二分能不能凑出来
所以
那么
然后关于这个
//--------------------------------------------------------------------------------
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
int k;
int pre[N], dp[N][7], to[N];
//--------------------------------------------------------------------------------
//struct or namespace:
//dp2数组是用来记录dp数组的前缀最小值的
int dp2[N][7];
//--------------------------------------------------------------------------------
bool check(int len) {
// seg[0].add(0, 0, 0);
rep(i, 0, k)
rep(j, 0, n) dp2[j][i] = INF;
dp2[0][0] = 0;
rep(j, 1, k) {
rep(i, 1, n) {
cmin(dp2[i][j - 1], dp2[i - 1][j - 1]);
}
rep(i, 1, n) {
if (i - len < 0) {
dp[i][j] = INF;
dp2[i][j] = dp[i][j];
continue;
}
if (to[i - len] - 1 < 0) dp[i][j] = INF;
else dp[i][j] = dp2[to[i - len] - 1][j - 1] + (pre[i] - pre[i - len]);
dp2[i][j] = dp[i][j];
}
}
int ans = INF;
rep(i, 1, n) cmin(ans, dp[i][k]);
if (ans <= m) return 1;
return 0;
}
signed main() {
fileRead();
kuaidu();
T = 1;
// cc(log2(200000) * log2(200000) * 200000 * 5);
//cin >> T;
while (T--) {
cin >> n >> m >> k;
string s;
cin >> s;
int len = s.size();
to[0] = 1;
if (s[0] == '0') to[1] = 1;
else to[1] = 0;
rep(i, 0, len-1) {
if (s[i] == '0') pre[i + 1]++;
pre[i + 1] += pre[i];
}
//to函数用来找到当前i点最左边的0的位置
rep(i, 2, n) {
if (s[i - 1] == '1') to[i] = to[i - 1];
else to[i] = i;
}
int l = -1, r = n + 1;
while (l + 1 != r) {
int mid = l + r >> 1;
if (check(mid)) l = mid;
else r = mid;
}
if (l == 0) l = -1;
cc(l);
}
return 0;
}
Problem I. Linguistics Puzzle
这个题不是太难,感觉难度上应该要比
一个小阅读理解,给你一个乘法表,让你推出来每个字母代表的是什么数字。最后写下来实际是一个码农题。
直接暴力枚举就好了,因为实际打表下来发现每个数字用到的次数并不是太多,我们可以用二元组数组
关于时间复杂度的计算不会,只是感觉得一个数字如果选择有
下面直接贴上码农屎山代码了:
//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
map<PII, vec_int> mp, mp2;
vec_int res;
//数字x在十位和个位上出现的次数是多少
PII biao[N];
// 字符i选择的数字是谁
int book[N];
//数字i有没有被选择
bool xuan[N];
//字符i的出现的次数
PII to[N];
//存下来字符串有哪些
vector<string> qs;
//--------------------------------------------------------------------------------
//struct or namespace:
//--------------------------------------------------------------------------------
void pre() {
rep(i, 0, n-1)
rep(j, 0, n-1) {
int l = i * j;
res.push_back(l);
if (l < n) {
biao[l].second++;
}
else {
biao[l / n].first++;
biao[l % n].second++;
}
}
sort(res.begin(), res.end());
rep(i, 0, n-1) {
mp[biao[i]].push_back(i);
}
}
int fan(char x) {
if (x <= 'z' and x >= 'a') return x - 'a';
return 26 + x - 'A';
}
char back(int x) {
if (x <= 25) return char(x + 'a');
return char(x - 26 + 'A');
}
bool check() {
vec_int tem;
for (auto &s: qs) {
if (s.size() == 1) tem.push_back(book[fan(s[0])]);
else {
tem.push_back(book[fan(s[0])] * n + book[fan(s[1])]);
}
}
sort(tem.begin(), tem.end());
rep(i, 0, n*n-1) {
if (tem[i] != res[i]) return 0;
}
return 1;
}
int dfs(int x) {
if (x == n) {
if (check()) return 1;
return 0;
}
for (auto &l: mp[to[x]]) {
if (xuan[l]) continue;
xuan[l] = 1;
book[x] = l;
if (dfs(x + 1)) return 1;
xuan[l] = 0;
}
return 0;
}
signed main() {
fileRead();
kuaidu();
T = 1;
cin >> T;
while (T--) {
cin >> n;
mp.clear();
mp2.clear();
res.clear();
rep(i, 0, n) {
to[i] = {0, 0};
xuan[i] = 0;
biao[i] = {0, 0};
}
qs.clear();
pre();
rep(i, 1, n*n) {
string s;
cin >> s;
qs.push_back(s);
if (s.size() == 1) {
to[fan(s[0])].second++;
}
else {
to[fan(s[0])].first++;
to[fan(s[1])].second++;
}
}
dfs(0);
rep(i, 0, n-1) {
rep(j, 0, n-1) {
if (book[j] == i) {
cout << back(j);
break;
}
}
}
cout << endl;
}
return 0;
}
Problem J. Takeout Delivering
赛时写的解法是先跑出来
貌似有人是用瓶颈树求的最小的最大值,好久没有写了,再换个做法写一下子吧。
正常的解法:
//--------------------------------------------------------------------------------
const int N = 3e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e18;
int n, m, T;
vector<PII> A[N];
bool vis[N];
int dis1[N], dis2[N];
//--------------------------------------------------------------------------------
//struct or namespace:
struct node {
int x;
int y;
int val;
};
vector<node> ed;
//--------------------------------------------------------------------------------
void dfs(int st,int dis[N]) {
rep(i, 1, n) dis[i] = INF, vis[i] = 0;
dis[st] = 0;
priority_queue<PII> F;
F.push({0, st});
while (!F.empty()) {
auto [_,x] = F.top();
F.pop();
if (vis[x]) continue;
vis[x] = 1;
for (auto &[y,val]: A[x]) {
if (dis[y] > max(val, dis[x])) {
dis[y] = max(val, dis[x]);
F.push({-val, y});
}
}
}
}
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n >> m;
rep(i, 1, m) {
int a, b, c;
cin >> a >> b >> c;
A[a].push_back({b, c});
A[b].push_back({a, c});
ed.push_back({a, b, c});
ed.push_back({b, a, c});
}
dfs(1, dis1);
dfs(n, dis2);
int ans = INF;
for (auto &[x,y,val]: ed) {
vec_int tem;
if (!(val >= dis1[x] and val >= dis2[y])) continue;
tem.push_back(val);
tem.push_back(dis1[x]);
tem.push_back(dis2[y]);
sort(tem.begin(), tem.end());
cmin(ans, tem[1] + tem[2]);
}
cc(ans);
}
return 0;
}
生成树解法:
(被这个办法卡空间了,感觉有点小阴间)
//--------------------------------------------------------------------------------
const int N = 6e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
//--------------------------------------------------------------------------------
//struct or namespace:
struct ED {
int x;
int y;
int val;
};
vector<ED> ed;
int D[N];
int tot;
namespace z {
vector<int> A[N];
int son[N], dep[N], fa[N];
//重儿子,顶根,时间戳,子树最右端时间戳,时间戳会对应节点x
int hea[N], up[N], dnf[N];
int tot;
void dfs(int x, int pa) {
son[x] = 1;
dep[x] = dep[pa] + 1;
fa[x] = pa;
int t = 0;
for (auto y: A[x]) {
if (y == pa) continue;
dfs(y, x);
son[x] += son[y];
if (!t or son[t] < son[y]) t = y;
}
hea[x] = t;
}
void dfs2(int x, int pa, int ding) {
dnf[x] = ++tot;
up[x] = ding;
if (hea[x]) dfs2(hea[x], x, ding);
for (auto y: A[x]) {
if (y == pa || y == hea[x]) continue;
dfs2(y, x, y);
}
}
void clear(int n) {
tot = 0;
rep(i, 1, n) {
A[i].clear();
hea[i] = up[i] = dnf[i] = 0;
}
}
void add(int x, int y, int c = 1) {
A[x].push_back(y);
}
int lca(int x, int y) {
while (up[x] != up[y]) {
if (dep[up[x]] < dep[up[y]]) swap(x, y);
x = fa[up[x]];
}
if (dep[x] > dep[y]) swap(x, y);
return x;
}
void work(int rt = 1) {
dfs(rt, 0);
dfs2(rt, 0, rt);
}
};
class DSU {
struct Info {
int fa;
};
Info dsu[N];
public:
Info &operator[](const int &x) { return dsu[find(x)]; }
void clear(int n) {
rep(i, 1, n) {
//TODO 初始化
dsu[i].fa = i;
}
}
void merge(int x, int y, int val) {
x = find(x), y = find(y);
if (x == y) return;
//TODO 合并操作
tot++;
dsu[x].fa = dsu[y].fa = tot;
z::add(tot, x);
z::add(tot, y);
D[tot] = val;
}
int find(int x) {
if (x == dsu[x].fa) return x;
return dsu[x].fa = find(dsu[x].fa);
}
bool same(int x, int y) {
x = find(x), y = find(y);
return (x == y);
}
};
DSU dsu;
int dis1[N], dis2[N];
//--------------------------------------------------------------------------------
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n >> m;
tot = n;
dsu.clear(n + n);
z::clear(n + n);
rep(i, 1, m) {
int a, b, c;
cin >> a >> b >> c;
ed.push_back({a, b, c});
}
sort(ed.begin(), ed.end(), [&](ED &q1, ED &q2) {
return q1.val < q2.val;
});
for (auto &[x,y,val]: ed) {
if (dsu.same(x, y)) continue;
dsu.merge(x, y, val);
}
z::work(tot);
rep(i, 1, n) {
int t = z::lca(1, i);
dis1[i] = D[t];
int t2 = z::lca(n, i);
dis2[i] = D[t2];
}
int ans = INF;
for (auto &[x,y,val]: ed) {
vec_int tem;
if (!(val >= dis1[x] and val >= dis2[y])) continue;
tem.push_back(val);
tem.push_back(dis1[x]);
tem.push_back(dis2[y]);
sort(tem.begin(), tem.end());
cmin(ans, tem[1] + tem[2]);
tem.clear();
if (!(val >= dis2[x] and val >= dis1[y])) continue;
tem.push_back(val);
tem.push_back(dis2[x]);
tem.push_back(dis1[y]);
sort(tem.begin(), tem.end());
cmin(ans, tem[1] + tem[2]);
}
cc(ans);
}
return 0;
}
PostScript
真的好颓废呢,
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战