A.Print 341(模拟)
题意:
给定一个正整数NNN,输出由NNN个0和(N+1)(N+1)(N+1)个1交替组成的字符串。
分析:
按题意模拟即可
代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
cout << 1;
for (int i = 0; i < n; i++) {
cout << "01";
}
cout << endl;
return 0;
}
B.Foreign Exchange(贪心)
题意:
有NNN个国家,编号为111到NNN。对于每个i=1,2,…,Ni=1,2,…,Ni=1,2,…,N,高桥拥有AiA_iAi个第iii个国家的货币。高桥可以重复执行以下操作任意次数(可能为零):
- 首先,在111和N−1N-1N−1(含两端)之间选择一个整数iii。然后,如果他拥有至少SiS_iSi个第iii个国家的货币,则他会执行以下操作一次:用SiS_iSi个第iii国家的货币交换TiT_iTi个下一个国家(i+1)(i+1)(i+1)的货币。
计算高桥最终可能拥有的第NNN个国家货币数量的最大值。
分析:
按照题意,按照题目所给规则一国一国模拟兑换即可。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL N = 2e5 + 10;
LL a[N];
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
for (int i = 0; i < n - 1; i++) {
int s, t;
cin >> s >> t;
a[i + 1] += a[i] / s * t;
}
cout << a[n - 1] << endl;
return 0;
}
C.Takahashi Gets Lost(模拟)
题意:
有一个HHH行WWW列的网格。网格的每个单元格都是陆地或海洋,由长度为WWW的HHH个字符串S1,S2,…,SHS_1,S2,…,S_HS1,S2,…,SH表示。(i,j)(i,j)(i,j)表示从上往下第iii行和从左往右第jjj列的单元格,并且如果SiS_iSi的第jjj个字符是. ,则(i,j)(i,j)(i,j)是陆地;如果该字符是#,则(i,j)(i,j)(i,j)是海洋。
题目约束保证网格周边(即满足i=1i=1i=1、i=Hi=Hi=H或j=1j=1j=1、j=Wj=Wj=W中至少一项的单元格(i,j)(i,j)(i,j))上所有单元都是海洋。
TakahashiTakahashiTakahashi的飞船在网格中某个单元格坠毁了。之后,他按照长度为NNN的字符串TTT中表示的指令移动NNN次,其中包括LLL、RRR、UUU和DDD 。对于i=1,2,…,Ni=1,2,…,Ni=1,2,…,N,TTT的第iii个字符描述如下:
- LLL表示向左移动一个单元格。也就是说,在进行此次移动之前若他在(i,j)(i,j)(i,j),那么此次移动后他将到达(i,j−1)(i,j−1)(i,j−1)处;
- RRR表示向右移动一个单元格。也就是说,在进行此次移动之前若他在(i,j)(i,j)(i,j),那么此次移动后他将到达(i,j+1)(i,j+1)(i,j+1)处;
- UUU表示向上移动一个单元格。也就是说,在进行此次移动之前若他在(i,j)(i,j)(i,j),那么此次移动后他将到达(i−1,j)(i−1,j)(i−1,j)处;
- DDD表示向下移动一个单元格。也就是说,在进行此次移动之前若他在(i,j)(i,j)(i,j),那么此次移动后他将到达(i+1,j)(i+1,j)(i+1,j)处。
已知沿着路径的所有单元格(包括坠毁并且当前所在位置)都不会被淹没,输出可能成为其当前位置的单元格数目。
分析:
题目要求按指令移动时不会经过海洋,即不会经过#。枚举位置,模拟操作序列,判断是否会经过#即可。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL mod = 998244353;
const LL N = 505;
char c[N][N];
int main() {
int h, w, n;
cin >> h >> w >> n;
string t;
cin >> t;
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
cin >> c[i][j];
}
}
int ans = 0;
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
if (c[i][j] == '#')
continue;
int a = i, b = j;
bool check = true;
for (int k = 0; k < n; k++) {
if (t[k] == 'L') {
if (b == 1) {
check = false;
break;
}
b -= 1;
if (c[a][b] == '#') {
check = false;
break;
}
} else if (t[k] == 'U') {
if (a == 1) {
check = false;
break;
}
a -= 1;
if (c[a][b] == '#') {
check = false;
break;
}
} else if (t[k] == 'R') {
if (b == w) {
check = false;
break;
}
b += 1;
if (c[a][b] == '#') {
check = false;
break;
}
} else {
if (a == h) {
check = false;
break;
}
a += 1;
if (c[a][b] == '#') {
check = false;
break;
}
}
}
if (check) {
ans++;
}
}
}
cout << ans << endl;
return 0;
}
D.Only one of two(二分)
题意:
给定三个正整数NNN、MMM和KKK。其中,NNN和MMM不同。
输出第KKK小的正整数,该正整数恰好可被NNN或MMM中的一个数整除。
分析:
先找到n,mn,mn,m的最小公倍数lll,那么对于一个数xxx,能被nnn整除且小于等于xxx的数的个数就是[x/n][x/n][x/n],所以可以推出(因为可能同时能被n,mn,mn,m整除,要删掉能被lll整除的数字个数)
[x/n]+[x/m]−2∗[x/l]>=k[x/n]+[x/m]-2*[x/l]>=k[x/n]+[x/m]−2∗[x/l]>=k
使用二分来进行查找即可。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
int main() {
LL n, m, x, k;
cin >> n >> m >> k;
x = (n * m) / gcd(n, m);
LL l = 0, r = (LL) 2e+20;
LL mid, cnt;
while ((l + 1) < r) {
mid = (l + r) / 2;
cnt = (mid / n) + (mid / m) - 2 * (mid / x);
if (cnt < k)
l = mid;
else
r = mid;
}
cout << r << endl;
return 0;
}
E.Alternating String(线段树)
题意:
定义好字符串是由0和1组成的字符串,其中两个连续字符总是不同的。
给你一个长度为NNN的字符串SSS,由0和1组成。
给出QQQ个查询,必须按顺序处理。
查询有两种类型:
1 L R:翻转SSS中第LLL到第RRR的每个字符。也就是说,对于每个满足L≤i≤RL\leq i\leq RL≤i≤R的整数iii,如果SSS中的第iii个字符是"1",则将其改为0,反之亦然。2 L R:假设S′S'S′是通过提取SSS的第LLL到第RRR个字符(不改变顺序)得到的长度为(R−L+1)(R-L+1)(R−L+1)的字符串。如果S′S'S′是一个好字符串,则输出Yes,否则输出No。
分析:
题目要求的好字符串必须是01交替出现。设数组xi=(si⊕si+1)x_i=(s_i \oplus s_{i+1})xi=(si⊕si+1),如果∑i=lr−1xi==r−l\sum\limits_{i=l}^{r-1}x_i==r-li=l∑r−1xi==r−l,那说明SSS的[l,r][l,r][l,r]是01交替的。对区间[l,r][l,r][l,r]进行翻转操作,不难发现所有的xix_ixi都不会发生变化。发生变化的只有两个边界,即xl−1x_{l-1}xl−1和xrx_rxr,显然这两个值会变成它们相反的数。我们注意到操作一对该数组的影响是两个单点修改,操作二是一个区间查询,用线段树维护数组即可。
代码:
#include<bits/stdc++.h>
using namespace std;
template<typename T>
class settree {
public:
vector<T> cnt;
int n;
settree(int _n) : n(_n) { cnt.resize(n); }
void modify(int x, T v) {
while (x < n) {
cnt[x] += v;
x |= (x + 1);
}
}
T get(int x) {
T v{};
while (x >= 0) {
v += cnt[x];
x = (x & (x + 1)) - 1;
}
return v;
}
T sum(int l, int r) {
T tot = get(r);
if (l != 0) {
tot -= get(l - 1);
}
return tot;
}
};
int main() {
int n, q;
cin >> n >> q;
string s;
cin >> s;
settree<int> sum(n - 1);
for (int i = 0; i < n - 1; i++) {
sum.modify(i, s[i] != s[i + 1]);
}
while (q--) {
int qu, l, r;
cin >> qu >> l >> r;
if (qu == 1) {
--l, --r;
if (l != 0) {
sum.modify(l - 1, sum.sum(l - 1, l - 1) == 1 ? -1 : 1);
}
if (r != (n - 1)) {
sum.modify(r, sum.sum(r, r) == 1 ? -1 : 1);
}
} else {
--l, --r;
if (sum.sum(l, r - 1) == (r - l))
cout << "Yes" << endl;
else
cout << "No" << endl;
}
}
return 0;
}
F.Breakdown(01背包)
题意:
给你一个由NNN个顶点和MMM条边组成的简单无向图。对于i=1,2,…,Mi=1,2,\ldots,Mi=1,2,…,M来说,第iii条边连接顶点uiu_iui和viv_ivi。另外,对于i=1,2,…,Ni=1,2,\ldots,Ni=1,2,…,N来说,顶点iii被赋值为正整数WiW_iWi,并且有AiA_iAi个碎片放在上面。
只要图上还有碎片,就重复下面的操作:
- 首先,从图形中选择一个点xxx并移除上面的一个碎片。
- 选择与xxx相邻的顶点集合SSS(可能为空),即∑y∈SWy<Wx\sum\limits_{y\in S}W_y\lt W_xy∈S∑Wy<Wx,并在SSS中的每个顶点上放置一个碎片。
输出此操作的最大次数。
题目保证无论如何操作,在有限次迭代后,图形上将没有棋子。
分析:
注意到操作里存在方向性即碎片总是往点权小的点跑。可以先求权值小的点,考虑权值大的点时,考虑放置碎片的点的答案都已经求出,因此可以求出该点的答案。
按点权小到大的顺序求解,假设dpidp_idpi表示点iii上有一个碎片带来的最大操作次数,考虑求解当前点dpidp_idpi时,就是考虑其wy≤wiw_y \le w_iwy≤wi的所有yyy。选哪些yyy,其∑dpy\sum dp_y∑dpy最大。
可以发现这样考虑本题即为01背包问题,因此就按照点权小到大的顺序求解nnn个01背包问题即可。
最后答案就是∑(dpi×ai)\sum (dp_i \times a_i)∑(dpi×ai)。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> edge(n);
int u, v;
for (int i = 0; i < m; i++) {
cin >> u >> v;
u--, v--;
edge[u].push_back(v);
edge[v].push_back(u);
}
vector<int> w(n);
for (auto &x: w)
cin >> x;
vector<int> a(n);
for (auto &x: a)
cin >> x;
vector<int> id(n);
iota(id.begin(), id.end(), 0);
sort(id.begin(), id.end(), [&](int x, int y) { return w[x] < w[y]; });
vector<int> cost(n);
LL ans = 0;
for (auto i: id) {
vector<array<int, 2>> goods;
for (auto j: edge[i]) {
if (w[j] < w[i])
goods.push_back({w[j], cost[j]});
}
if (goods.empty()) {
cost[i] = 1;
} else {
vector<int> dp(w[i], 0);
for (auto &[x, y]: goods) {
for (int j = w[i] - 1; j >= x; j--) {
dp[j] = max(dp[j], dp[j - x] + y);
}
}
cost[i] = 1 + *max_element(dp.begin(), dp.end());
}
ans += 1LL * a[i] * cost[i];
}
cout << ans << endl;
return 0;
}
G.Highest Ratio(计算几何)
题意:
给你一个长度为NNN的序列A=(A1,A2,…,AN)A=(A_1,A_2,\ldots,A_N)A=(A1,A2,…,AN)。
求每个k=1,2,…,Nk=1,2,\ldots,Nk=1,2,…,N的解:
- 当选择一个整数rrr使得k≤r≤Nk\leq r\leq Nk≤r≤N时,求序列AAA中第kkk到第rrr项的最大平均值。
序列AAA的第kkk到第rrr项的平均值定义为1r−k+1∑i=krAi\frac{1}{r-k+1}\displaystyle\sum\limits_{i=k}^r A_ir−k+11i=k∑rAi。
分析:
考虑二维平面上编号为0,1,2,…,N0,1,2,\ldots,N0,1,2,…,N的(N+1)(N+1)(N+1)个点。
点000的坐标为(x0,y0)=(0,0)(x_0,y_0)=(0,0)(x0,y0)=(0,0),点iii的坐标为(xi,yi)=(i,A1+A2+⋯+Ai)(x_i,y_i)=(i,A_1+A_2+\cdots +A_i)(xi,yi)=(i,A1+A2+⋯+Ai)。
那么AL,AL+1,…,ARA_L,A_{L+1},\ldots,A_RAL,AL+1,…,AR的平均值就表示为点L−1{L-1}L−1和点RRR的斜率。
对于固定的kkk(1≤k≤N)(1\leq k\leq N)(1≤k≤N),考虑如何求出序列AAA中第kkk到第rrr项的最大平均值。
取点k−1,k,k+1,…,N{k-1},k,k+1,\ldots,Nk−1,k,k+1,…,N中的凸包。如果点k−1,k,k+1,…,N{k-1},k,k+1,\ldots,Nk−1,k,k+1,…,N是共线的,那么是Ak=Ak+1=⋯ ANA_k=A_{k+1}=\cdots~A_NAk=Ak+1=⋯ AN,所以平均值是AkA_kAk。凸包是一个面积为正的区域。点k−1{k-1}k−1是唯一一个坐标最小为xxx的点,位于凸包的边界上。从点(k−1)(k-1)(k−1)顺时针绕边界一周后,如果下一个点是ppp,那么答案就是yp−yk−1xp−xk−1\frac{y_p-y_{k-1}}{x_p-x_{k-1}}xp−xk−1yp−yk−1。这是因为,从点(k−1)(k-1)(k−1)逆时针时,如果下一个点是qqq,那么对于构成凸包的点iii(k≤i≤N)(k\leq i\leq N )(k≤i≤N),答案是yp−yk−1xp−xk−1\frac{y_p-y_{k-1}}{x_p-x_{k-1}}xp−xk−1yp−yk−1。根据凸包的定义,经过点iii和(k−1)(k-1)(k−1)的直线的斜率至少是点qqq和点(k−1)(k-1)(k−1)的斜率,最多是点ppp和(k−1)(k-1)(k−1)的斜率。我们所求的值相当于连接点iii(k≤i≤N)(k\leq i\leq N)(k≤i≤N)和点(k−1)(k-1)(k−1)的直线的最大斜率。所以答案是yp−yk−1xp−xk−1\frac{y_p-y_{k-1}}{x_p-x_{k-1}}xp−xk−1yp−yk−1,也就是连接点ppp和(k−1)(k-1)(k−1)的直线(k−1)(k-1)(k−1)的斜率。
毕竟,对于每个点kkk,只需考虑点k−1,k+1,…,N{k-1},k+1,\ldots,Nk−1,k+1,…,N的凸包,并找出从点(k−1)(k-1)(k−1)顺时针方向遍历凸包边界时遇到的下一个点即可;换句话说,就是构成上边界的点序列中点(k−1)(k-1)(k−1)的下一个点。
如果直接为每个kkk寻找凸包,会超时。但是,在按此顺序添加点N,N−1,…,0N,N-1,\ldots,0N,N−1,…,0的同时求凸包上边界时,可以求出经过点k−1{k-1}k−1的直线的斜率,以及构成上边界的点序列中点k−1{k-1}k−1旁边的点。这样,就可以找到所有所需的值,其时间复杂度与只找到一次由点0,1,…,N0,1,\ldots,N0,1,…,N组成的点集的凸包的时间复杂度相同。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL mod = 998244353;
const LL N = 2e5 + 5;
LL pre[N], arr[N], q[N];
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
}
reverse(arr + 1, arr + n + 1);
for (int i = 1; i <= n; i++) {
pre[i] = pre[i - 1] + arr[i];
}
vector<double> ans;
int l = 0, r = -1;
q[++r] = 0;
for (int i = 1; i <= n; i++) {
while (l < r && (pre[i] - pre[q[r]]) * (q[r] - q[r - 1]) <= (pre[q[r]] - pre[q[r - 1]]) * (i - q[r]))
r--;
ans.push_back(1.0 * (pre[i] - pre[q[r]]) / (i - q[r]));
q[++r] = i;
}
for (auto it = ans.rbegin(); it != ans.rend(); ++it) {
cout << fixed << setprecision(10) << *it << endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

907





