AtCoder Beginner Contest 352 考试总结
前言
正常发挥。属于是 \(4\) 个月没搞 OI,复健成功了!
得分明细:
A | B | C | D | E | F | G | Total |
---|---|---|---|---|---|---|---|
√ | √ | √ | √ | √ | × | × | 1475 |
改题明细:
A | B | C | D | E | F | G |
---|---|---|---|---|---|---|
√ | √ | √ | √ | √ | × | × |
第一次正式 rated 打 AT,行吧!
A. AtCoder Line
Problem
AtCoder 铁路线有 \(N\) 个车站,编号为 \(1, 2, \ldots, N\)。
在这条线路上,有趟进站列车从 \(1\) 站出发,依次停靠 \(2, 3, \ldots, N\) 站,有趟出站列车从 \(N\) 站出发,依次停靠 \(N - 1, N - 2, \ldots, 1\) 站。
高桥站即将从 \(X\) 站前往 \(Y\) 站,只需使用进站和出站列车中的一列。
求列车在 \(Z\) 站停留的时间。
Solve
当 \(\{z\in [x,y]|x\le y\}\) 即为 "Yes",否则即为 "No"。
Code
#include <bits/stdc++.h>
#define ll long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007
using namespace std;
inline int read() {
rint x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void print(int x){
if(x<0){putchar('-');x=-x;}
if(x>9){print(x/10);putchar(x%10+'0');}
else putchar(x+'0');
return;
}
int n, x, y, z;
signed main() {
n = read(), x = read(), y = read(), z = read();
if(x > y) swap(x, y);
if(x <= z && z <= y) puts("Yes");
else puts("No");
return 0;
}
B. Typing
Problem
高桥尝试使用键盘输入由小写英文字母组成的字符串 \(S\)。
他打字时只看键盘,不看屏幕,实际键入的字符串是 \(T\)。
\(T\) 中没有被误输入的字符称为正确输入字符。
确定正确键入的字符在 \(T\) 中的位置。
Solve
指针 \(i\) 指着 \(S\) 一边判一边扫即可。
Code
#include <bits/stdc++.h>
#define ll long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007
using namespace std;
inline int read() {
rint x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void print(int x){
if(x<0){putchar('-');x=-x;}
if(x>9){print(x/10);putchar(x%10+'0');}
else putchar(x+'0');
return;
}
const int N = 2e5 + 10;
int n, m;
char s[N], t[N];
signed main() {
cin >> (s + 1) >> (t + 1);
n = strlen(s + 1), m = strlen(t + 1);
int i = 1;
For(j,1,m) {
if(s[i] == t[j]) cout << j << ' ', i++;
}
return 0;
}
C. Standing On The Shoulders
Problem
有 \(N\) 个巨人,他们的名字分别是 \(1\) 到 \(N\)。当巨人 \(i\) 站在地上时,他们的肩高是 \(A_i\),头高是 \(B_i\)。
你可以选择 \((1, 2, \ldots, N)\) 的 \((P_1, P_2, \ldots, P_N)\) 排列组合,并根据以下规则堆叠 \(N\) 个巨人:
- 首先,将巨人 \(P_1\) 放在地上。巨人 \(P_1\) 的肩膀距离地面的高度为 \(A_{P_1}\),头部距离地面的高度为 \(B_{P_1}\)。
- 为了 \(i = 1, 2, \ldots, N - 1\) 的顺序,要把巨人 \(P_{i + 1}\) 放在巨人 \(P_i\) 的肩膀上。如果巨人 \(P_i\) 的肩膀距离地面的高度是 \(t\),那么巨人 \(P_{i + 1}\) 的肩膀距离地面的高度就是 \(t + A_{P_{i + 1}}\),他们的头距离地面的高度就是 \(t + B_{P_{i + 1}}\)。
求最上面的巨人 \(P_N\) 的头部距离地面的最大可能高度。
Solve
考虑到每种排列的叠加总高度为某 \(n-1\) 个巨人的肩高加上剩下 \(1\) 个巨人的头高。
则答案为:\(\max\limits_{1\le i \le n}\sum\limits_{j=1,i\not=j}^{n}A_j+B_i\)。
Code
#include <bits/stdc++.h>
#define int long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007
using namespace std;
inline int read() {
rint x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void print(int x){
if(x<0){putchar('-');x=-x;}
if(x>9){print(x/10);putchar(x%10+'0');}
else putchar(x+'0');
return;
}
const int N = 2e5 + 10;
struct Node {
int a, b;
} a[N];
int n, res = 0, ans;
signed main() {
n = read();
For(i,1,n) a[i].a = read(), a[i].b = read(), res += a[i].a;
For(i,1,n) {
ans = max(ans, res - a[i].a + a[i].b);
}
cout << ans << '\n';
return 0;
}
D. Permutation Subsequence
Problem
给你一个 \((1, 2, \dots, N)\) 的排列组合 \(P = (P_1, P_2, \dots, P_N)\)。
如果一个索引序列 \((i_1, i_2, \dots, i_K)\) 同时满足以下两个条件,那么这个索引序列被称为好索引序列:
- \(1 \leq i_1<i_2<\dots<i_K \leq N\)。
- 子序列 \((P_{i_1}, P_{i_2}, \dots, P_{i_K})\) 可以通过重新排列一些连续的 \(K\) 整数而得到。
形式上,存在一个整数 \(a\) ,使得 \(\lbrace P_{i_1},P_{i_2},\dots,P_{i_K} \rbrace = \lbrace a,a+1,\dots,a+K-1 \rbrace\)。
求所有好的索引序列中 \(i_K - i_1\) 的最小值。可以证明,在此问题的约束条件下,至少存在一个好的索引序列。
Solve
记 \(a_{p_i} = i\),发现答案贡献区间为一个宽度为 \(k\) 的窗口。
滑动窗口求 max/min 即可。
Code
#include <bits/stdc++.h>
#define int long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007
using namespace std;
inline int read() {
rint x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void print(int x){
if(x<0){putchar('-');x=-x;}
if(x>9){print(x/10);putchar(x%10+'0');}
else putchar(x+'0');
return;
}
const int N = 2e5 + 10;
int n, k, p[N], a[N], q[N], Max[N], Min[N], ans = INT_MAX;
signed main() {
n = read(), k = read();
For(i,1,n) p[i] = read(), a[p[i]] = i;
int h = 1, t = 0;
For(i,1,n) {
while(h <= t && i - q[h] + 1 > k) h++;
while(h <= t && a[i] < a[q[t]]) t--;
q[++t] = i;
if(i >= k) {
Min[i] = a[q[h]];
}
}
h = 1, t = 0;
For(i,1,n) {
while(h <= t && i - q[h] + 1 > k) h++;
while(h <= t && a[i] > a[q[t]]) t--;
q[++t] = i;
if(i >= k) {
Max[i] = a[q[h]];
}
}
For(i,k,n) {
ans = min(ans, Max[i] - Min[i]);
}
cout << ans << '\n';
return 0;
}
E. Clique Connect
Problem
给你一个加权无向图 \(G\),其中有 \(N\) 个顶点,编号为 \(1\) 至 \(N\) 。最初,\(G\) 没有边。
您将执行 \(M\) 次操作为 \(G\) 添加边。\(i\)-th 操作 \((1 \leq i \leq M)\) 如下:
- 给你一个由 \(K_i\) 个顶点组成的顶点子集 \(S_i=\lbrace A_{i,1},A_{i,2},\dots,A_{i,K_i}\rbrace\)。对于每一对 \(u, v\) 这样的顶点 \(u, v \in S_i\) 和 \(u < v\),在顶点 \(u\) 和 \(v\) 之间添加一条边,边的权重为 \(C_i\)。
完成所有 \(M\) 操作后,确定 \(G\) 是否相连。如果是,求 \(G\) 最小生成树中各条边的总重。
Solve
一个组内的点与其连边组成的图必为一个团,团内两两联通,且边权相等。
考虑 kruskal,先决策边权最小的团。然后暴力团内 kruskal。
加两个限制:
- 如果当前点已经在团内联通,就不需要加边了,跳过此回合;
- 如果此时生成树的边数已经为 \(n-1\),结束决策;
记得先判连通,再进行决策,不然最后一点会 T 掉。(赛时没注意罚时 \(4\) 次)
然后就 A 了。
Code
#include <bits/stdc++.h>
#define int long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007
using namespace std;
inline int read() {
rint x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void print(int x){
if(x<0){putchar('-');x=-x;}
if(x>9){print(x/10);putchar(x%10+'0');}
else putchar(x+'0');
return;
}
const int N = 4e5 + 10;
struct Eg {
int k, c;
} b[N];
int n, m, f[N], p[N], ans, net;
vector<int> a[N];
bool vis[N];
bool cmp(int x, int y) {
return b[x].c < b[y].c;
}
int find(int x) {
return (x == f[x] ? x : f[x] = find(f[x]));
}
signed main() {
n = read(), m = read();
For(i,1,n) f[i] = i;
For(i,1,m) {
p[i] = i;
b[i] = (Eg){read(), read()};
For(j,1,b[i].k) {
int x = read();
a[i].push_back(x);
f[find(a[i][0])] = find(a[i][a[i].size()-1]);
}
}
int res = 0;
For(i,1,n) {
if(find(i) != find(1)) res++;
}
if(res) return puts("-1"), 0;
For(i,1,n) f[i] = i;
sort(p + 1, p + m + 1, cmp);
For(i,1,m) {
int tg = i; i = p[i];
For(j,0,b[i].k-1) {
if(vis[find(a[i][j])]) continue;
For(k,j+1,b[i].k-1) {
int u = a[i][j], v = a[i][k];
int fu = find(u), fv = find(v);
if(fu == fv) continue;
f[fu] = fv; vis[fu] = 1;
ans += b[i].c; net++;
if(net == n-1) goto yzsy;
}
}
i = tg;
}
yzsy: ;
cout << ans << '\n';
return 0;
}
F. Clique Connect
Problem
有 \(N\) 人,编号为 \(1\) 至 \(N\)。
在这些 \(N\) 人中举行了一次竞赛,并对他们进行了相应的排名。有关他们的排名信息如下:
- 每个人都有一个唯一的排名。
- 对于每个 \(1 \leq i \leq M\),如果 \(A_i\) 的排名是 \(x\) -th,而 \(B_i\) 的排名是 \(y\)-th,那么就是 \(x - y = C_i\)。
给定的输入保证了至少有一种可能的排名与给定的信息不矛盾。
回答 \(N\) 个查询。 \(i\)-th查询的答案是一个整数,确定方法如下:
- 如果可以唯一确定人 \(i\) 的排名,则返回该排名。否则,返回 \(-1\)。
Solve
Code
G. Socks 3
Problem
高桥的抽屉里有各种颜色的袜子。袜子的颜色用从 \(1\) 到 \(N\) 的整数表示,其中有 \(A_i\geq 2\) 只袜子的颜色是 \(i\)。
他准备通过执行以下操作来选择今天要穿的袜子:
- 继续以相等的概率从箱子中每次随机抽取一只袜子,直到他能从已抽取的袜子中做出一双相同颜色的袜子为止。一旦抽到袜子,就不能再放回箱子里。
求他从箱子中抽取袜子的次数的期望值(模为 \(998244353\))。