A.Glutton Takahashi(模拟)
题意:
高桥打算吃
N
N
N 道菜。
如果
S
i
=
S_i =
Si=sweet
是 “甜的”,那么他打算吃的
i
i
i 道菜就是甜的;如果
S
i
=
S_i =
Si=salty
是 “咸的”,那么他打算吃的
i
i
i 道菜就是咸的。咸的。
如果他连续吃了两道甜菜,他就会感到不适,并且无法再吃任何菜肴。
判断他是否能吃下所有菜肴。
分析:
如果有两个连续sweet
出现在字符串中,不包括末尾,那么为Yes
,否则为No
。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
string s[105];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
while (t--)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
}
for (int i = 2; i <= n; i++)
{
if (s[i] == "sweet" && s[i - 1] == "sweet" && i != n)
{
cout << "No" << endl;
return 0;
}
}
cout << "Yes" << endl;
}
return 0;
}
B.Grid Walk(模拟)
题意:
有一个网格,网格中有 H H H 行和 W W W 列。让 ( i , j ) (i, j) (i,j) 表示从上往下数第 i i i 行和从左往上数第 j j j 列的单元格。
如果
C
i
,
j
C_{i, j}
Ci,j 是 .
,那么单元格
(
i
,
j
)
(i, j)
(i,j) 就是空的,如果
C
i
,
j
C_{i, j}
Ci,j 是 #
,那么单元格
(
i
,
j
)
(i, j)
(i,j) 就不是空的。
高桥目前位于 ( S i , S j ) (S_i, S_j) (Si,Sj) 单元格,他将按照以下规则依次对 i = 1 , 2 , … , ∣ X ∣ i = 1, 2, \ldots, |X| i=1,2,…,∣X∣ 采取行动。
- 如果
X
X
X 的
i
i
i个字符是
L
,并且当前单元格左边的单元格是空的,那么他会移动到左边的单元格。否则,他将留在当前单元格中。 - 如果
X
X
X 的
i
i
i个字符是
R
,并且当前单元格右边的单元格是空的,那么他将移动到右边的单元格。否则,他将留在当前的单元格中。 - 如果
X
X
X 的
i
i
i个字符是
U
,并且当前单元格的上方存在空格,那么他将移动到上方的单元格。否则,他将留在当前的单元格中。 - 如果
X
X
X 的
i
i
i个字符是
D
,并且当前单元格下方存在空格,那么他将移动到下方的单元格。否则,他将停留在当前单元格。
输出他完成一系列操作后所在的单元格。
分析:
按照题目要求模拟即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
while (t--)
{
int n, m;
cin >> n >> m;
int sx, sy;
cin >> sx >> sy;
vector<vector<char>> g(n + 1, vector<char>(m + 1));
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> g[i][j];
}
}
string op;
cin >> op;
for (int i = 0; i < op.length(); i++)
{
char x = op[i];
if (x == 'D')
{
if (sx + 1 <= n && g[sx + 1][sy] == '.')
{
sx++;
}
}
else if (x == 'U')
{
if (sx - 1 > 0 && g[sx - 1][sy] == '.')
{
sx--;
}
}
else if (x == 'R')
{
if (sy + 1 <= m && g[sx][sy + 1] == '.')
{
sy++;
}
}
else if (x == 'L')
{
if (sy - 1 > 0 && g[sx][sy - 1] == '.')
{
sy--;
}
}
}
cout << sx << ' ' << sy << endl;
}
return 0;
}
C.Minimum Glutton(贪心)
题意:
有 N N N 道菜,其中 i i i 道菜的甜度为 A i A_i Ai ,咸度为 B i B_i Bi 。
高桥打算将这些 N N N 菜肴按照自己喜欢的顺序排列,然后按照这个顺序吃掉。
他将按照排列顺序吃掉这些菜肴,但是一旦他吃掉的菜肴的总甜度超过 X X X 或总咸度超过 Y Y Y ,他就会停止进食。
求他最后吃掉的菜肴的最少数量。
分析:
为了让更早结束,我们显然只需要考虑其中一种属性。将甜度与咸度分离,分别排序,取符合条件的最小值即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
LL a[maxn], b[maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
while (t--)
{
LL n, x, y;
cin >> n >> x >> y;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
cin >> b[i];
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + n);
LL ans = n;
LL tmp = 0;
for (int i = n; i >= 1; i--)
{
tmp += a[i];
ans = i;
if (tmp > x)
break;
}
tmp = 0;
LL ans1 = 0;
for (int i = n; i >= 1; i--)
{
tmp += b[i];
ans1 = i;
if (tmp > y)
break;
}
cout << n - max(ans, ans1) + 1 << endl;
}
return 0;
}
D.K-th Nearest (二分)
题意:
在一条数线上有 N + Q N+Q N+Q 个点 A 1 , … , A N , B 1 , … , B Q A_1,\dots,A_N,B_1,\dots,B_Q A1,…,AN,B1,…,BQ ,其中点 A i A_i Ai 的坐标为 a i a_i ai ,点 B j B_j Bj 的坐标为 b j b_j bj 。
就每个 j = 1 , 2 , … , Q j=1,2,\dots,Q j=1,2,…,Q 回答下面的问题:
- 设 X X X 是 A 1 , A 2 , … , A N A_1,A_2,\dots,A_N A1,A2,…,AN 中最靠近点 B j B_j Bj 的 k j k_j kj点。求点 X X X 与 B j B_j Bj 之间的距离。更具体地说,设 d i d_i di 是点 A i A_i Ai 与 B j B_j Bj 之间的距离。将 ( d 1 , d 2 , … , d N ) (d_1,d_2,\dots,d_N) (d1,d2,…,dN) 按升序排序,得到序列 ( d 1 ′ , d 2 ′ , … , d N ′ ) (d_1',d_2',\dots,d_N') (d1′,d2′,…,dN′) 。求 d k j ′ d_{k_j}' dkj′ .
分析:
我们先将 a i a_i ai排序,对于每一个 b i b_i bi,我们可以二分这个距离是 x x x,然后看 [ t m p − m , t m p + m ] [tmp−m , tmp+m] [tmp−m,tmp+m]的点的数量,如果是大于等于 k k k,则说明该距离是第 k k k小或更大距离,则往小的方向收缩。而统计点的数量则通过两次二分点坐标即可
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
int a[maxn], b[maxn], k[maxn];
int tmp, num, n, q;
bool check(int x)
{
int idxhigh = upper_bound(a + 1, a + 1 + n, tmp + x) - a;
int idxlow = lower_bound(a + 1, a + 1 + n, tmp - x) - a;
idxhigh--;
int res = idxhigh - idxlow + 1;
return res >= num;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
while (t--)
{
cin >> n >> q;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= q; i++)
cin >> b[i] >> k[i];
sort(a + 1, a + 1 + n);
for (int i = 1; i <= q; i++)
{
tmp = b[i];
num = k[i];
int low = 0, high = 2e8;
while (low < high)
{
int mid = low + high >> 1;
if (check(mid))
{
high = mid;
}
else
low = mid + 1;
}
cout << high << endl;
}
}
return 0;
}
E.Maximum Glutton (dp)
题意:
高桥为斯努克准备了
N
N
N 道菜。菜肴的编号从
1
1
1 到
N
N
N ,菜肴
i
i
i 的甜度为
A
i
A_i
Ai ,咸度为
B
i
B_i
Bi 。
高桥可以按照自己喜欢的顺序排列这些菜肴。斯努克会按照排列顺序吃掉这些菜肴,但如果他吃过的菜肴的总甜度超过
X
X
X 或总咸度超过
Y
Y
Y ,他就不会再吃任何菜肴。
高桥希望斯努克吃尽可能多的菜肴。求如果高桥把菜肴摆放得最合理,斯努克最多吃的菜肴数。
分析:
设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示第 i i i个菜品,甜度为 j j j时的最小咸度,有转移方程为: d p [ j ] [ k ] = m i n ( d p [ j ] [ k ] , d p [ j − 1 ] [ k − a ] + b ) ; dp[j][k] = min(dp[j][k], dp[j - 1][k - a] + b); dp[j][k]=min(dp[j][k],dp[j−1][k−a]+b);。最后只要求得 d p [ i ] [ j ] ≤ y dp[i][j] \le y dp[i][j]≤y的最大的 i i i即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
const int N = 1e2 + 10, M = 1e4 + 10;
int dp[N][M], n, a, b, x, y;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
while (t--)
{
cin >> n >> x >> y;
memset(dp, 0x3f, sizeof dp);
for (int i = 0; i <= x; i++)
dp[0][i] = 0;
for (int i = 1; i <= n; i++)
{
cin >> a >> b;
for (int j = i; j; j--)
{
for (int k = x; k >= a; k--)
{
dp[j][k] = min(dp[j][k], dp[j - 1][k - a] + b);
}
}
}
for (int i = n - 1; i; i--)
{
for (int j = 0; j <= x; j++)
{
if (dp[i][j] <= y)
{
cout << i + 1 << endl;
return 0;
}
}
}
}
cout << 1 << endl;
return 0;
}
F.Range Connect MST (图论)
题意:
有一个图,它有
N
+
Q
N + Q
N+Q 个顶点,编号为
1
,
2
,
…
,
N
+
Q
1, 2, \ldots, N + Q
1,2,…,N+Q 。最初,该图没有边。
对于这个图,请依次对
i
=
1
,
2
,
…
,
Q
i = 1, 2, \ldots, Q
i=1,2,…,Q 执行以下操作:
- 对于每个满足 L i ≤ j ≤ R i L_i \leq j \leq R_i Li≤j≤Ri 的整数 j j j ,在顶点 N + i N + i N+i 和 j j j 之间添加一条代价为 C i C_i Ci 的无向边。
完成所有操作后,确定图形是否相连。如果相连,求该图的最小生成树的代价。
分析:
考虑是否构成连通块,即这
q
q
q个线段是否构成一个大线段。再考虑最小生成树怎么求,即考虑前
n
n
n个点该和哪个操作点
(
n
+
i
)
(n+i)
(n+i)连边。
我们从代价小的操作开始,给
L
i
≤
j
≤
R
i
L_i \le j \le R_i
Li≤j≤Ri 中的每个点合并成一个联通块,每合并一次的代价是
C
i
C_i
Ci。最后看是否是同个连通块即可。
连通块利用并查集维护,合并时总是以编号大的点为根,这样在上述从左到右合并时每次都从还未连通的点开始遍历即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
int fa[maxn], rmax[maxn];
int n, q;
struct node
{
int l, r, c;
} a[maxn];
bool cmp(node p, node q)
{
return p.c < q.c;
}
LL ans;
int find(int x)
{
if (fa[x] == x)
return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
int fx = find(x), fy = find(y);
if (fx != fy)
{
fa[fx] = fy;
rmax[fy] = max(rmax[fx], rmax[fy]);
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
while (t--)
{
cin >> n >> q;
for (int i = 1; i <= n; ++i)
fa[i] = rmax[i] = i;
for (int i = 1; i <= q; ++i)
{
cin >> a[i].l >> a[i].r >> a[i].c;
}
sort(a + 1, a + 1 + q, cmp);
for (int i = 1; i <= q; ++i)
{
int now = a[i].l;
while (1)
{
ans += 1ll * a[i].c;
int nxt = rmax[find(now)] + 1;
if (nxt > a[i].r)
break;
else
{
merge(now, nxt);
now = nxt;
}
}
}
if (rmax[find(1)] != n)
cout << -1 << endl;
else
cout << ans << endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。