A.TLD(模拟)
题意:
给一个字符串 s s s,输出最后一个’.'位置后面的所有字符。
分析:
循环找到最后一个’.'的输出即可。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
int pos = 0;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '.') pos = i;
}
for (int i = pos + 1; i < s.size(); i++) {
cout << s[i];
}
cout << endl;
return 0;
}
B.Langton’s Takahashi(模拟)
题意:
给出一个二维网格图,左上点为起点,初始网格图为白色。进行 n n n次如下操作之后输出网格图的颜色。
- 将格子颜色黑白翻转
- 格子原色为白色,顺时针旋转 90 ° 90° 90°,前进一个格子。
- 格子原色为黑色,逆时针旋转 90 ° 90° 90°,前进一个格子。
分析:
网格图很小,可以先定义好方向数组,用取模的方式来表示方向的改变。
代码:
#include <bits/stdc++.h>
using namespace std;
int h, w, n;
const int MAXN = 1e3 + 5;
int map1[MAXN][MAXN];
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
int main() {
cin >> h >> w >> n;
int x = 0, y = 0, dir = 0;
while (n--) {
map1[x][y] ^= 1;
if (map1[x][y] == 0) {
dir = (dir - 1 + 4) % 4;
} else {
dir = (dir + 1 + 4) % 4;
}
x = (x + dx[dir] + h) % h;
y = (y + dy[dir] + w) % w;
}
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
if (map1[i][j] == 0)
cout << ".";
else
cout << "#";
}
cout << endl;
}
return 0;
}
C.Perfect Bus(思维)
题意:
公交车沿途停 n n n站,给出每站上车的人,如果是正数表示有人上车,负数表示有人下车。要求车上人数时刻不小于 0 0 0。询问最后公交车上最少剩几个人。
分析:
求最后人数最少相当于求最初车上人数最少。假设一开始是 0 0 0人,模拟公交车过程计算公交车人数的最小值。设最小值为 x x x,如果 x < 0 x<0 x<0表示公交车上一开始至少需要 x x x人才能保持人数合法。如果 x > 0 x>0 x>0就是最终答案。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 2e5 + 5;
LL a[MAXN];
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
LL minval = 0;
LL sum = 0;
for (int i = 1; i <= n; i++) {
sum += a[i];
minval = min(minval, sum);
}
cout << sum - minval << endl;
return 0;
}
D Synchronized Players(bfs)
题意:
给出一张 n ∗ n n*n n∗n的网格图,网格图有部分格子是障碍物,询问至少进行多少次如下操作使得两个玩家移动到同一个格子:
- 选定上下左右一个确定方向,两个玩家同时沿这个方向移动一格,如果下一格是障碍物或出界则不移动。
分析:
由于数据很小,用 v i s [ p l a y 1 x ] [ p l a y 1 y ] [ p l a y 2 x ] [ p l a y 2 y ] vis[play1x][play1y][play2x][play2y] vis[play1x][play1y][play2x][play2y]表示是否走过,利用 b f s bfs bfs进行爆搜即可,注意边界判断。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 65;
char mp1[MAXN][MAXN];
int vis[MAXN][MAXN][MAXN][MAXN];
int play1x = -1, play1y = -1, play2x = -1, play2y = -1;
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, -1, 0, 1};
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> mp1[i][j];
if (mp1[i][j] == 'P') {
if (play1x == -1) {
play1x = i, play1y = j;
} else {
play2x = i, play2y = j;
}
}
}
}
int ans = 1e9;
queue<array<int, 4>> q;
q.push({play1x, play1y, play2x, play2y});
vis[play1x][play1y][play2x][play2y] = 1;
while (!q.empty()) {
auto [x1, y1, x2, y2] = q.front();
q.pop();
if (x1 == x2 && y1 == y2) {
ans = min(ans, vis[x1][y1][x2][y2] - 1);
}
for (int i = 0; i < 4; i++) {
int dx1 = x1 + dx[i], dy1 = y1 + dy[i];
int dx2 = x2 + dx[i], dy2 = y2 + dy[i];
int nx1, nx2, ny1, ny2;
if (dx1 >= 0 && dx1 < n && dy1 < n && dy1 >= 0 && mp1[dx1][dy1] != '#') {
nx1 = dx1, ny1 = dy1;
} else {
nx1 = x1, ny1 = y1;
}
if (dx2 >= 0 && dx2 < n && dy2 < n && dy2 >= 0 && mp1[dx2][dy2] != '#') {
nx2 = dx2, ny2 = dy2;
} else {
nx2 = x2, ny2 = y2;
}
if (vis[nx1][ny1][nx2][ny2])
continue;
vis[nx1][ny1][nx2][ny2] = vis[x1][y1][x2][y2] + 1;
q.push({nx1, ny1, nx2, ny2});
}
}
if (ans == 1e9)
cout << -1 << endl;
else
cout << ans << endl;
return 0;
}
E Smooth Subsequence(dp+线段树)
题意:
给一个数组 a a a和常数 d d d,询问最少需要删除多少个数可以使得剩余的数字两两之差的绝对值不超过 d d d。
分析:
d p [ i ] dp[i] dp[i]表示前 i i i个元素并且以第 i i i个元素作为结尾的最大子序列长度。这是一个 n 2 n^2 n2的转移,第二维可以优化成 [ a [ i ] − d , a [ i ] + d ] [a[i]-d,a[i]+d] [a[i]−d,a[i]+d]区间中取 d p [ j ] dp[j] dp[j]的最大值,最大值可以用线段树进行维护。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e6 + 5;
#define ls p << 1
#define rs p << 1 | 1
const int maxn = 5e5;
LL num[N], a[N], tag[N];
LL b[N];
void build(int s, int t, int p) {
if (s == t) {
a[p] = num[s];
return;
}
int m = (s + t) / 2;
build(s, m, ls);
build(m + 1, t, rs);
a[p] = max(a[ls], a[rs]);
}
void push_down(int s, int t, int p) {
if (!tag[p] || s == t)
return;
int m = (s + t) / 2;
a[ls] += tag[p];
a[rs] += tag[p];
tag[ls] += tag[p];
tag[rs] += tag[p];
tag[p] = 0;
}
void change(int l, int r, LL c, int s, int t, int p) {
if (l <= s && t <= r) {
a[p] += (LL) c;
tag[p] += c;
return;
}
int m = (s + t) / 2;
push_down(s, t, p);
if (m >= l)
change(l, r, c, s, m, ls);
if (m < r)
change(l, r, c, m + 1, t, rs);
a[p] = max(a[ls], a[rs]);
}
LL ask(int l, int r, int s, int t, int p) {
if (l <= s && t <= r)
return a[p];
push_down(s, t, p);
int m = (s + t) / 2;
LL sum = 0;
if (m >= l)
sum = ask(l, r, s, m, ls);
if (m < r)
sum = max(sum, ask(l, r, m + 1, t, rs));
return sum;
}
int main() {
int n, d;
cin >> n >> d;
for (int i = 1; i <= n; i++)
cin >> b[i];
build(1, maxn, 1);
int ans = 1;
for (int i = 1; i <= n; i++) {
int l = max((LL) 1, b[i] - d), r = min((LL) maxn, b[i] + d);
int num = ask(l, r, 1, maxn, 1);
ans = max(ans, num + 1);
if (num + 1 > ask(b[i], b[i], 1, maxn, 1))
change(b[i], b[i], num + 1 - ask(b[i], b[i], 1, maxn, 1), 1, maxn, 1);
}
cout << ans << endl;
return 0;
}
F Product Equality(状压 dp)
题意:
给定一个长度为 n n n的数组 a a a,询问满足下列式子的三元组数量
- a i × a j = a k a_i \times a_j = a_k ai×aj=ak ( 1 ≤ i , j , k ≤ n ) (1 \le i,j,k \le n) (1≤i,j,k≤n)
分析:
n ≤ 1000 n\le 1000 n≤1000考虑 n 2 n^2 n2做法。 a i × a j × = a k a_i \times a_j \times =a_k ai×aj×=ak可以转化成 a i % m o d × a j % m o d = a k % m o d a_i \% mod \times a_j \%mod =a_k \%mod ai%mod×aj%mod=ak%mod。于是就可以对读入的 a i a_i ai进行取模,再通过 n u m [ i ] num[i] num[i]统计数值 i i i出现的次数, n 2 n^2 n2进行枚举。取模要用双哈希进行,防止哈希冲突
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod1 = 998244353, mod2 = 1e7 + 7;
#define PII pair<LL, LL>
int main() {
int n;
cin >> n;
vector<PII > a(n + 1);
map<PII, LL> mp1;
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
LL tmp1 = 0;
for (int j = 0; j < s.size(); j++) {
tmp1 = tmp1 * 10 + s[j] - '0';
tmp1 %= mod1;
}
LL tmp2 = 0;
for (int j = 0; j < s.size(); j++) {
tmp2 = tmp2 * 10 + s[j] - '0';
tmp2 %= mod2;
}
a[i] = {tmp1, tmp2};
mp1[{tmp1, tmp2}]++;
}
LL ans = 0;
for (int x = 1; x <= n; x++) {
for (int y = 1; y <= n; y++) {
LL z1 = a[x].first * a[y].first % mod1;
LL z2 = a[x].second * a[y].second % mod2;
ans += mp1[{z1, z2}];
}
}
cout << ans << endl;
return 0;
}
G Smaller Sum(主席树)
题意:
给定一个长度为 n n n的数组 a i a_i ai,回答 q q q个询问:
- 给出 l , r , x l,r,x l,r,x求 [ l , r ] [l,r] [l,r]区间内所有小于等于 x x x的 a i a_i ai的和。
要求强制在线。
分析:
将上述条件转化成 s u m 1 sum1 sum1为 [ 1 , r ] [1,r] [1,r]区间内所有小于等于 x x x的 a i a_i ai的和, s u m 2 sum2 sum2为 [ 1 , l − 1 ] [1,l-1] [1,l−1]区间内所有小于等于 x x x的 a i a_i ai的和,上述要求所求为 s u m 1 − s u m 2 sum1-sum2 sum1−sum2。因此考虑线段树,由于需要获得历史版本信息,所以需要用到主席树。每次询问变成两棵历史版本的线段树的区间查询的差。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 2e5 + 10;
#define ls(x) tr[x].l
#define rs(x) tr[x].r
struct node {
LL l, r;
LL sum;
} tr[MAXN * 40];
LL a[MAXN], b[MAXN];
LL root[MAXN], num;
void push(int x) {
tr[x].sum = tr[ls(x)].sum + tr[rs(x)].sum;
}
void build(LL &x, LL l, LL r) {
x = ++num;
if (l == r)
return;
int m = l + r >> 1;
build(ls(x), l, m);
build(rs(x), m + 1, r);
}
void change(LL x, LL &y, LL l, LL r, LL v) {
y = ++num;
tr[y] = tr[x];
if (l == r) {
tr[y].sum += b[v];
return;
}
LL m = l + r >> 1;
if (v <= m)
change(ls(x), ls(y), l, m, v);
else
change(rs(x), rs(y), m + 1, r, v);
push(y);
}
LL ask(LL L, LL R, LL l, LL r, LL q, LL w) {
if (q > w)
return 0;
if (q <= l && r <= w)
return tr[R].sum - tr[L].sum;
LL m = l + r >> 1;
LL s = 0;
if (q <= m)
s += ask(tr[L].l, tr[R].l, l, m, q, w);
if (w > m)
s += ask(tr[L].r, tr[R].r, m + 1, r, q, w);
return s;
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
b[i] = a[i];
}
sort(b + 1, b + n + 1);
for (int i = 1; i <= n; i++)
a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
build(root[0], 1, n);
for (int i = 1; i <= n; i++)
change(root[i - 1], root[i], 1, n, a[i]);
int m;
cin >> m;
LL tmp = 0;
while (m--) {
LL l, r, x;
cin >> l >> r >> x;
l ^= tmp, r ^= tmp, x ^= tmp;
LL s = upper_bound(b + 1, b + n + 1, x) - b - 1;
tmp = ask(root[l - 1], root[r], 1, n, 1, s);
cout << tmp << endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。