A.TLD(模拟)
题意:
给一个字符串sss,输出最后一个’.'位置后面的所有字符。
分析:
循环找到最后一个’.'的输出即可。
代码:
#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(模拟)
题意:
给出一个二维网格图,左上点为起点,初始网格图为白色。进行nnn次如下操作之后输出网格图的颜色。
- 将格子颜色黑白翻转
- 格子原色为白色,顺时针旋转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(思维)
题意:
公交车沿途停nnn站,给出每站上车的人,如果是正数表示有人上车,负数表示有人下车。要求车上人数时刻不小于000。询问最后公交车上最少剩几个人。
分析:
求最后人数最少相当于求最初车上人数最少。假设一开始是000人,模拟公交车过程计算公交车人数的最小值。设最小值为xxx,如果x<0x<0x<0表示公交车上一开始至少需要xxx人才能保持人数合法。如果x>0x>0x>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∗nn*nn∗n的网格图,网格图有部分格子是障碍物,询问至少进行多少次如下操作使得两个玩家移动到同一个格子:
- 选定上下左右一个确定方向,两个玩家同时沿这个方向移动一格,如果下一格是障碍物或出界则不移动。
分析:
由于数据很小,用vis[play1x][play1y][play2x][play2y]vis[play1x][play1y][play2x][play2y]vis[play1x][play1y][play2x][play2y]表示是否走过,利用bfsbfsbfs进行爆搜即可,注意边界判断。
代码:
#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+线段树)
题意:
给一个数组aaa和常数ddd,询问最少需要删除多少个数可以使得剩余的数字两两之差的绝对值不超过ddd。
分析:
dp[i]dp[i]dp[i]表示前iii个元素并且以第iii个元素作为结尾的最大子序列长度。这是一个n2n^2n2的转移,第二维可以优化成[a[i]−d,a[i]+d][a[i]-d,a[i]+d][a[i]−d,a[i]+d]区间中取dp[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)
题意:
给定一个长度为nnn的数组aaa,询问满足下列式子的三元组数量
- ai×aj=aka_i \times a_j = a_kai×aj=ak (1≤i,j,k≤n)(1 \le i,j,k \le n)(1≤i,j,k≤n)
分析:
n≤1000n\le 1000n≤1000考虑n2n^2n2做法。ai×aj×=aka_i \times a_j \times =a_kai×aj×=ak可以转化成ai%mod×aj%mod=ak%moda_i \% mod \times a_j \%mod =a_k \%modai%mod×aj%mod=ak%mod。于是就可以对读入的aia_iai进行取模,再通过num[i]num[i]num[i]统计数值iii出现的次数,n2n^2n2进行枚举。取模要用双哈希进行,防止哈希冲突
代码:
#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(主席树)
题意:
给定一个长度为nnn的数组aia_iai,回答qqq个询问:
- 给出l,r,xl,r,xl,r,x求[l,r][l,r][l,r]区间内所有小于等于xxx的aia_iai的和。
要求强制在线。
分析:
将上述条件转化成sum1sum1sum1为[1,r][1,r][1,r]区间内所有小于等于xxx的aia_iai的和,sum2sum2sum2为[1,l−1][1,l-1][1,l−1]区间内所有小于等于xxx的aia_iai的和,上述要求所求为sum1−sum2sum1-sum2sum1−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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

1704

被折叠的 条评论
为什么被折叠?



