A.Glutton Takahashi(模拟)
题意:
高桥打算吃 NNN 道菜。
如果 Si=S_i =Si=sweet
是 “甜的”,那么他打算吃的 iii 道菜就是甜的;如果 Si=S_i =Si=salty
是 “咸的”,那么他打算吃的 iii 道菜就是咸的。咸的。
如果他连续吃了两道甜菜,他就会感到不适,并且无法再吃任何菜肴。
判断他是否能吃下所有菜肴。
分析:
如果有两个连续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(模拟)
题意:
有一个网格,网格中有 HHH 行和 WWW 列。让 (i,j)(i, j)(i,j) 表示从上往下数第 iii 行和从左往上数第 jjj 列的单元格。
如果 Ci,jC_{i, j}Ci,j 是 .
,那么单元格 (i,j)(i, j)(i,j) 就是空的,如果 Ci,jC_{i, j}Ci,j 是 #
,那么单元格 (i,j)(i, j)(i,j) 就不是空的。
高桥目前位于 (Si,Sj)(S_i, S_j)(Si,Sj) 单元格,他将按照以下规则依次对 i=1,2,…,∣X∣i = 1, 2, \ldots, |X|i=1,2,…,∣X∣ 采取行动。
- 如果 XXX 的 iii个字符是
L
,并且当前单元格左边的单元格是空的,那么他会移动到左边的单元格。否则,他将留在当前单元格中。 - 如果 XXX 的 iii个字符是
R
,并且当前单元格右边的单元格是空的,那么他将移动到右边的单元格。否则,他将留在当前的单元格中。 - 如果 XXX 的 iii个字符是
U
,并且当前单元格的上方存在空格,那么他将移动到上方的单元格。否则,他将留在当前的单元格中。 - 如果 XXX 的 iii个字符是
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(贪心)
题意:
有 NNN 道菜,其中 iii 道菜的甜度为 AiA_iAi ,咸度为 BiB_iBi 。
高桥打算将这些 NNN 菜肴按照自己喜欢的顺序排列,然后按照这个顺序吃掉。
他将按照排列顺序吃掉这些菜肴,但是一旦他吃掉的菜肴的总甜度超过 XXX 或总咸度超过 YYY ,他就会停止进食。
求他最后吃掉的菜肴的最少数量。
分析:
为了让更早结束,我们显然只需要考虑其中一种属性。将甜度与咸度分离,分别排序,取符合条件的最小值即可。
代码:
#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+QN+QN+Q 个点 A1,…,AN,B1,…,BQA_1,\dots,A_N,B_1,\dots,B_QA1,…,AN,B1,…,BQ ,其中点 AiA_iAi 的坐标为 aia_iai ,点 BjB_jBj 的坐标为 bjb_jbj 。
就每个 j=1,2,…,Qj=1,2,\dots,Qj=1,2,…,Q 回答下面的问题:
- 设 XXX 是 A1,A2,…,ANA_1,A_2,\dots,A_NA1,A2,…,AN 中最靠近点 BjB_jBj 的 kjk_jkj点。求点 XXX 与 BjB_jBj 之间的距离。更具体地说,设 did_idi 是点 AiA_iAi 与 BjB_jBj 之间的距离。将 (d1,d2,…,dN)(d_1,d_2,\dots,d_N)(d1,d2,…,dN) 按升序排序,得到序列 (d1′,d2′,…,dN′)(d_1',d_2',\dots,d_N')(d1′,d2′,…,dN′) 。求 dkj′d_{k_j}'dkj′ .
分析:
我们先将aia_iai排序,对于每一个bib_ibi,我们可以二分这个距离是xxx,然后看[tmp−m,tmp+m][tmp−m , tmp+m][tmp−m,tmp+m]的点的数量,如果是大于等于kkk,则说明该距离是第kkk小或更大距离,则往小的方向收缩。而统计点的数量则通过两次二分点坐标即可
代码:
#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)
题意:
高桥为斯努克准备了 NNN 道菜。菜肴的编号从 111 到 NNN ,菜肴 iii 的甜度为 AiA_iAi ,咸度为 BiB_iBi 。
高桥可以按照自己喜欢的顺序排列这些菜肴。斯努克会按照排列顺序吃掉这些菜肴,但如果他吃过的菜肴的总甜度超过 XXX 或总咸度超过 YYY ,他就不会再吃任何菜肴。
高桥希望斯努克吃尽可能多的菜肴。求如果高桥把菜肴摆放得最合理,斯努克最多吃的菜肴数。
分析:
设dp[i][j]dp[i][j]dp[i][j]表示第iii个菜品,甜度为jjj时的最小咸度,有转移方程为: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);dp[j][k]=min(dp[j][k],dp[j−1][k−a]+b);。最后只要求得dp[i][j]≤ydp[i][j] \le ydp[i][j]≤y的最大的iii即可。
代码:
#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+QN + QN+Q 个顶点,编号为 1,2,…,N+Q1, 2, \ldots, N + Q1,2,…,N+Q 。最初,该图没有边。
对于这个图,请依次对 i=1,2,…,Qi = 1, 2, \ldots, Qi=1,2,…,Q 执行以下操作:
- 对于每个满足 Li≤j≤RiL_i \leq j \leq R_iLi≤j≤Ri 的整数 jjj ,在顶点 N+iN + iN+i 和 jjj 之间添加一条代价为 CiC_iCi 的无向边。
完成所有操作后,确定图形是否相连。如果相连,求该图的最小生成树的代价。
分析:
考虑是否构成连通块,即这qqq个线段是否构成一个大线段。再考虑最小生成树怎么求,即考虑前nnn个点该和哪个操作点(n+i)(n+i)(n+i)连边。
我们从代价小的操作开始,给 Li≤j≤RiL_i \le j \le R_iLi≤j≤Ri 中的每个点合并成一个联通块,每合并一次的代价是 CiC_iCi。最后看是否是同个连通块即可。
连通块利用并查集维护,合并时总是以编号大的点为根,这样在上述从左到右合并时每次都从还未连通的点开始遍历即可。
代码:
#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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。