第四届成都信息工程大学天梯赛

写了一半,群里发出来官方题解了……但是我码风比他的好,可读性强一点,但是为了强迫症和留作记录,我必须写完😇。所以这篇博客的性质已经从题解 transform 成赛后的感想了。
个人感觉有点像是水赛的感觉,L1 非常顺利,L2 和 L3 的最后两道开始调不出来,最终结果是 190 分(我的 L3-1 的 30 分还被队友吃了😭),L1 拿满,L2 的 3,4 WA,L3 的 2 TLE,3 没看。
我的 windows 还没回来,所以用的机房的编译一下就死机的电脑(但这不是我没存代码的理由),只能事后重敲一遍代码了,顺便回忆一下当时的心情。
L1 基础级
L1 的 8 道题都非常顺利,没有被卡,可惜速度还是没有 xx liu (qwertyuiop) 快。
L1-1 遇见YFffffff
print('Hello YFffffff')
L1-2 桃之夭夭,灼灼其华
n = int(input())
s = max(map(int, input().split()))
print(s, 'sad' if s & 1 else 'love')
L1-3 体温预警系统
n = int(input())
if n:
t = [0 for i in range(4)]
s = ['zhengchang', 'dire', 'gaore', 'yichangshuju']
for i in range(n):
tmp = round(float(input()) * 10)
if tmp in range(360, 373):
t[0] += 1
elif tmp in range(373, 381):
t[1] += 1
elif tmp in range(381, 411):
t[2] += 1
else:
t[3] += 1
print('\n'.join(f'{s[i]}:{t[i]}' for i in range(4) if t[i]))
else:
print('wuxiaoshuju')
L1-4 破碎的心,无法挽回的距离
#include <iostream>
using namespace std;
int a[110];
int main() {
int n, res = 0x3f3f3f3f;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
for (int p = 1; p <= 100; ++p) {
int s = 0;
for (int i = 1; i <= n; ++i) {
s += (a[i] - p) * (a[i] - p);
}
res = min(res, s);
}
cout << res << endl;
return 0;
}
L1-5 心碎?抽卡时间!
a = list(map(int, input().split()))
b = list(map(int, input().split()))
for i in range(4):
if a[i] < b[i]:
print('spider YFffffff')
break
else:
a[i + 1] += (a[i] - b[i]) // 5
else:
print(a[4] if a[4] else 'QAQ')
L1-6 字符串糕手
l = int(input())
s = input()
res = 1000
for i in range(l // 2, l):
t = s[:i] + s[i::-1]
res = min(res, i * 2 + 1 - l) if s == (s[:i] + s[i::-1])[:l] else res
res = min(res, i * 2 - l) if s == (s[:i] + s[i - 1::-1])[:l] else res
print(res)
L1-7 若敢来氪,必叫你大败而归
l = []
n, x = map(int, input().split())
for i in range(n):
a = input().split()
l.append((-int(a[1]), -int(a[2]), -int(a[3]), int(a[4]), a[0]))
cnt = 0
l.sort()
for i in l:
if x - i[3] >= 0:
print(i[4])
x -= i[3]
cnt += 1
else:
break
print(cnt)
L1-8 回到她的身边好吗
n, m, k, p = map(int, input().split())
a = [(0, 0)]
for i in range(m):
a.append(tuple(map(int, input().split())))
a.append((n, n))
a.sort()
for i in range(1, len(a)):
if a[i][0] - a[i - 1][1] > k:
if p and k:
p, k = p - 1, k - 1
else:
print('buguanle', a[i - 1][1])
break
else:
print('YES', k)
这个时候 L1 顺着做完了,还在沾沾自喜。
L2 进阶级
实话说我感觉不是很困难的,但是 wa 了两道题。
L2-1 来自YFffffff的挑战
这个策略当时有一定蒙的成分,实际上也是正确的。
#include <iostream>
using namespace std;
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
int n;
string s, t = "";
cin >> n >> s;
if (n == 1 || s[1] >= s[0]) cout << s[0] << s[0] << endl;
else {
for (char c : s) {
if (t.empty() || c <= t.back()) t += c;
else break;
}
cout << t;
for (int i = t.length() - 1; i >= 0; --i) cout << t[i];
cout << endl;
}
}
return 0;
}
L2_2 不要刁难我们了
最短路板子题,做到这儿的时候也还是非常顺利,这时候心态已经被机房电脑磨的差不多了,这次不怪电脑怪我没事先拷过来一份新的编译器,我在本地调试的时候因为 >>>
不能连续,auto 遍历 vector 不能用被折磨的要死,还好是一遍过了。
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 200010;
vector<pair<int, int>> ed[N];
int w[N];
long long dis[N], cnt[N];
bool vis[N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m, s, t;
cin >> n >> m >> s >> t;
for (int i = 1; i <= n; ++i) cin >> w[i];
for (int i = 1; i <= m; ++i) {
int x, y, z;
cin >> x >> y >> z;
ed[x].push_back({z, y});
ed[y].push_back({z, x});
}
priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> q;
memset(dis, 0x3f, sizeof(dis));
cnt[s] = 1;
dis[s] = 0;
q.push({dis[s], s});
while (!q.empty()) {
int x = q.top().second;
q.pop();
if (vis[x]) continue;
vis[x] = true;
for (auto [v, y] : ed[x]) {
if (dis[y] > dis[x] + w[y] + v) {
dis[y] = dis[x] + w[y] + v;
cnt[y] = cnt[x];
q.push({dis[y], y});
}
else if (dis[y] == dis[x] + w[y] + v) {
cnt[y] += cnt[x];
}
}
}
cout << dis[t] << endl << cnt[t] << endl;
return 0;
}
L2-3 花非花,雾非雾
我 wa 掉的做法是开了个队列存储边,有更改的时候从更改的点出发 dfs 更新所有能更新的点,但是问题在于我 dfs 的路径并不一定是按照边从前到后更新的;其实当时又想到用并查集,但是最后又暂时放弃了。
这道题最后的有效关系图一定是一个森林,每个连通子图都是有向树,边从根指向叶子,其中只有根结点的 a 已经给出,从根到叶子跑一遍 dfs 就可以更新出所有点的 a;用并查集可以维护一个子图内是否已经有一个 a 已经给出的根节点,对于一个 U 关系,两个已经有根的子图连在一起或者子图内部连边可能会矛盾,即使不矛盾这条边也是多余的,所以全部都判定为无效即可。
- ps: dfs 会爆栈,别问我怎么知道的。
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int N = 40010;
vector<pair<int, int>> ed[N];
int fa[N], a[N];
bool f[N]; // 维护是否已经有根
int getfa(int x) {
return x == fa[x] ? x : fa[x] = getfa(fa[x]);
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) fa[i] = i;
while (m--) {
char c;
cin >> c;
if (c == 'U') {
int x, y, w;
cin >> x >> y >> w;
int px = getfa(x), py = getfa(y);
if (px == py || (f[px] & f[py])) continue;
fa[py] = px;
f[px] ^= f[py];
ed[x].push_back({w, y});
ed[y].push_back({w, x});
}
else {
int x, w;
cin >> x >> w;
if (!f[getfa(x)]) {
a[x] = w;
f[getfa(x)] = true;
}
}
}
queue<int> q;
for (int i = 1; i <= n; ++i) {
if (a[i]) q.push(i);
}
while (!q.empty()) {
int x = q.front();
q.pop();
for (auto [v, y] : ed[x]) {
if (a[y]) continue;
a[y] = a[x] ^ v;
q.push(y);
}
}
for (int i = 1; i <= n; ++i) {
if (!a[i]) {
cout << "sad" << endl;
return 0;
}
}
for (int i = 1; i <= n; ++i) cout << a[i] << ' ';
cout << endl;
return 0;
}
L2-4 是留不住你的冰寒飞影
我现在还认为滑动窗口是可行的,考场上写的可能逻辑还是有点小问题,本质上我当时写的单调队列只是没有具象化的把点合并了。照着题解的思路写完代码之后发现我当时的问题是维护的范围小了,一个滑动窗口能 0 代价到达的最左端和最右端由左侧第二个和右侧第二个点决定,而不是窗口的左右端点。
#include <iostream>
using namespace std;
const int N = 1000010;
int a[N];
int l[N], r[N], t;
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, k, m;
scanf("%d%d%d", &n, &k, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
t = 0;
a[0] = -0x3f3f3f3f, a[n + 1] = 0x3f3f3f3f;
for (int i = 1; i <= n; ++i) {
if (i == 1 && a[i + 1] - a[i] > k || i == n && a[i] - a[i - 1] > k) {
t++;
l[t] = r[t] = a[i];
}
else if (a[i] - a[i - 1] <= k) {
l[t] = min(l[t], a[i] - k);
r[t] = max(r[t], a[i - 1] + k);
}
else if (a[i + 1] - a[i] <= k) {
t++;
l[t] = r[t] = a[i];
}
}
for (int i = 2; i <= t; ++i) {
m -= (l[i] - r[i - 1] + k - 1) / k;
}
if (m >= 0) printf("Yes\n");
else printf("No\n");
}
return 0;
}
L3 登顶级
L3-1 银白之森
想到敲了二十多分钟基环树 dp 代码,我自己都想笑😇😇😇。这是一张后继图,每个点初度为1,所以构成基环树,然后我就想偏了。其实不用区分环内环外直接一起倍增预处理一下然后把 k 二进制分解算就行。还是忘不了之前一道树上倍增 + 环形dp 的基环树题😮💨,我写着写着发现环里面还得倍增,然后用了同一个倍增数组,又写了一会儿才发现问题的严重性,直接把代码全删了重写了一遍。
#include <iostream>
using namespace std;
const int N = 100010;
int f[N][50];
long long g[N][50];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &f[i][0]);
g[i][0] = f[i][0];
}
for (int j = 1; j < 50; ++j) {
for (int i = 1; i <= n; ++i) {
f[i][j] = f[f[i][j - 1]][j - 1];
g[i][j] = g[i][j - 1] + g[f[i][j - 1]][j - 1];
}
}
while (m--) {
int x;
long long k, res = 0;
scanf("%d%lld", &x, &k);
for (int i = 0; i < 50; ++i) {
if (k >> i & 1) {
res += g[x][i];
x = f[x][i];
}
}
printf("%lld\n", res);
}
return 0;
}
L3-2 摸球游戏
当时矩阵快速幂 t 了,按说不该 t 的;但是这不是重点,重点是:我要向我高中数学老师道歉😭,这道题是一阶线性递推求通项,我把他当二阶的了,甚至还试图找特征方程。2025/03/07 晚上 22:00 我突然意识到了问题的严重性,于是一个不动点求出来等比数列的递推式然后秒了。他的题解太麻烦了,其实这就是一道平平无奇的高中概率题,果然上大学🧠会退化。
计球的总个数为 n,i 次操作后的期望为 \(E_i\),根据期望递推,有
\[E_i = \frac{E_{i - 1}}{n}E_{i - 1} + \frac{n - E_{i - 1}}{n}(E_{i - 1} + 1)\]
化简得
\[E_i = \frac{n - 1}{n}E_{i - 1} + 1\]
计算不动点,解方程 \(x = \frac{n - 1}{n}x + 1\)得,\(x= n\). 所以有
\[E_i - n = \frac{n - 1}{n}(E_{i - 1} - n)\]
后面就不用我教了,首项是 n - a,公比是 \(\frac{n - 1}{n}\)等比数列通向公式直接求 \(E_k\)即可。
对不起 90 老师,过了半年就忘干净了😭😭😭.
#include <iostream>
using namespace std;
int MOD = 1000000007;
long long power(long long n, long long p) {
long long res = 1, base = n;
while (p) {
if (p & 1) res = res * base % MOD;
base = base * base % MOD;
p >>= 1;
}
return res;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
long long a, b, c, k;
cin >> T;
while (T--) {
cin >> a >> b >> k;
b += a;
cout << (((a - b + MOD) % MOD * power((b - 1) * power(b, MOD - 2) % MOD, k)) % MOD + b + MOD) % MOD << endl;
}
return 0;
}
L3-3 电荷
这道自己做是真做不出来,正确的结论是一个 D 合法当且仅当按 x 排序检查和按 y 排序检查至少有一个可以通过,其余情况通过适当交换可以转化成按 x 和 y 检查的一种。如果我自己想只能想到按其中一个排序,调代码的时候也是调的非常头疼。
代码还可以压一压,排序和前缀后缀 max 和 min 可以写成一个函数。但是我已经不想看这个💩了
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 100010;
pair<int, int> a[N];
int pre_mn[N], pre_mx[N], pos_mn[N], pos_mx[N];
int n;
long long pow2(int t) {
return (long long)t * t;
}
long long calc(int l, int r) {
if (l == 1 && r == n) return 0;
int mn = 0x3f3f3f3f, mx = -0x3f3f3f3f;
if (l != 1) mn = min(mn, pre_mn[l - 1]), mx = max(mx, pre_mx[l - 1]);
if (r != n) mn = min(mn, pos_mn[r + 1]), mx = max(mx, pos_mx[r + 1]);
return max(pow2(mx - mn), max(pow2(mx), pow2(mn)) + max(pow2(a[l].first), pow2(a[r].first)));
}
bool check(long long mid) {
for (int i = 1, j = 1; i <= n; ++i) {
while (j <= n && (long long)(a[j].first - a[i].first) * (a[j].first - a[i].first) <= mid) {
if (calc(i, j) <= mid) return true;
j++;
}
if (calc(i, j - 1) <= mid) return true;
}
return false;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &a[i].first, &a[i].second);
}
sort(a + 1, a + n + 1);
pre_mn[0] = 0x3f3f3f3f;
pre_mx[0] = -0x3f3f3f3f;
pos_mn[n + 1] = 0x3f3f3f3f;
pos_mx[n + 1] = -0x3f3f3f3f;
for (int i = 1; i <= n; ++i) {
pre_mx[i] = max(pre_mx[i - 1], a[i].second);
pre_mn[i] = min(pre_mn[i - 1], a[i].second);
}
for (int i = n; i; --i) {
pos_mx[i] = max(pos_mx[i + 1], a[i].second);
pos_mn[i] = min(pos_mn[i + 1], a[i].second);
}
long long l = 0, r = 80000000000000000;
while (l < r) {
long long mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
long long res = l;
for (int i = 1; i <= n; ++i) swap(a[i].first, a[i].second);
sort(a + 1, a + n + 1);
pre_mn[0] = 0x3f3f3f3f;
pre_mx[0] = -0x3f3f3f3f;
pos_mn[n + 1] = 0x3f3f3f3f;
pos_mx[n + 1] = -0x3f3f3f3f;
for (int i = 1; i <= n; ++i) {
pre_mx[i] = max(pre_mx[i - 1], a[i].second);
pre_mn[i] = min(pre_mn[i - 1], a[i].second);
}
for (int i = n; i; --i) {
pos_mx[i] = max(pos_mx[i + 1], a[i].second);
pos_mn[i] = min(pos_mn[i + 1], a[i].second);
}
l = 0, r = 80000000000000000;
while (l < r) {
long long mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
res = min(res, l);
printf("%lld\n", res);
return 0;
}
终于补完了,奖励自己歇一晚上😴😴😴
Subscribe to my newsletter
Read articles from Star directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Star
Star
A Chinese OIer and a tosser.