A. Special Characters (思维)
题意:
给你一个整数 nnn 。构造一个由大写字母组成的字符串。这个字符串中必须有nnn个特殊字符。如果一个字符与其相邻的一个字符相等,我们就将其称为特殊字符。
例如,在 AAABAACC 字符串中有 666 个特殊字符(分别位于 111 、 333 、 555 、 666 、 777 和 888 )。
输出任何满足题意的字符串。
分析:
奇数情况无解。偶数情况通过AABBAABBAABB循环的形式进行构造。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
if (n & 1)
cout << "NO" << endl;
else {
cout << "YES" << endl;
string ans;
for (int i = 1, j = 1; i <= n; i += 2, j++) {
if (j & 1)
ans += "AA";
else
ans += "BB";
}
cout << ans << endl;
}
}
return 0;
}
B.Array Fix (贪心)
题意:
给你一个长度为 nnn 的整数数组 aaa 。
你可以执行以下操作任意多次(可能为零):取数组 aaa 中至少是 101010 的任意元素,删除它,然后在相同位置插入该元素包含的数字,按它们在该元素中出现的顺序排列。
- 如果我们对数组 [12,3,45,67][12, 3, 45, 67][12,3,45,67] 中的 第333个元素执行此操作,那么数组就变成了 [12,3,4,5,67][12, 3, 4, 5, 67][12,3,4,5,67] 。
- 如果我们对数组 [2,10][2, 10][2,10] 中的 第222个元素执行此操作,那么数组就变成了 [2,1,0][2, 1, 0][2,1,0] 。
你的任务是确定是否有可能通过任意次上述操作使 aaa 以非降序排序。换句话说,你必须确定是否有可能将数组 aaa 转换为 a1≤a2≤⋯≤aka_1 \le a_2 \le \dots \le a_ka1≤a2≤⋯≤ak ,其中 kkk 是数组 aaa 的当前长度。
分析:
我们从后往前倒推,记录前一个的值,如果比当前值大就尝试拆当前值。拆了值只会变小,所以尽可能不拆。
代码:
#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9;
int a[105];
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
bool flag = 1;
int r = INF;
for (int i = n - 1; i >= 0 && flag; i--) {
int x = a[i];
if (a[i] <= r)
r = a[i];
else {
while (x) {
int y = x % 10;
if (y <= r)
r = y;
else {
flag = 0;
break;
}
x /= 10;
}
}
}
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
C. Arrow Path (bfs)
题意:
有一个网格,由 222 行和 nnn 列组成。行的编号从上到下从 111 到 222 。列的编号从左到右依次为 111 至 nnn 。网格的每个单元格都包含一个箭头,指向左边或右边。没有箭头指向网格外。
有一个机器人从 (1,1)(1, 1)(1,1) 格开始。每隔一秒钟,下面两个动作会相继发生:
- 首先,机器人向左、向右、向下或向上移动(不能试图移动到网格外,也不能跳过移动);
- 然后,机器人沿着放置在当前单元格中的箭头移动。
判断机器人能否到达 (2,n)(2, n)(2,n) 单元格。
分析:
bfsbfsbfs的过程中,将每一步加入队列的过程改成每两步加入一次队列即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
string mp[2];
int vis[2][N], dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
unordered_map<char, int> dir;
int main() {
int t;
cin >> t;
dir['<'] = 3;
dir['>'] = 1;
while (t--) {
int n;
cin >> n;
for (int i = 0; i < 2; i++) {
cin >> mp[i];
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < n; j++) {
vis[i][j] = 0;
}
}
vis[0][0] = 1;
queue<pair<LL, LL> > q;
q.push({0, 0});
bool flag = false;
while (q.size()) {
auto tmp = q.front();
q.pop();
int x = tmp.first, y = tmp.second;
if (x == 1 && y == n - 1) {
flag = 1;
break;
}
for (int i = 0; i < 4; i++) {
int X = x + dx[i], Y = y + dy[i];
if (X < 0 || X > 1 || Y < 0 || Y > n - 1)
continue;
int X2 = X + dx[dir[mp[X][Y]]], Y2 = Y + dy[dir[mp[X][Y]]];
if (X2 < 0 || X2 > 1 || Y2 < 0 || Y2 > n - 1)
continue;
if (!vis[X2][Y2]) {
vis[X2][Y2] = 1;
q.push({X2, Y2});
}
}
}
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
D.Tandem Repeats? (dp)
题意:
给你一个由小写拉丁字母或问号组成的字符串 sss ,串联重复是一个偶数长度的字符串,其前半部分等于后半部分。
现在要求用某个小写英文字母替换每个问号,使串联重复的最长子串的长度尽可能最大。
分析:
dp[i][j]dp[i][j]dp[i][j]表示从iii开始的后缀和从jjj开始的后缀的最大前缀和,当符合s[i]==′?′∣∣s[j]==′?′∣∣s[i]==s[j]s[i] == '?' || s[j] == '?' || s[i] == s[j]s[i]==′?′∣∣s[j]==′?′∣∣s[i]==s[j]的时候,有转移方程dp[i][j]=dp[i+1][j+1]+1;dp[i][j] = dp[i + 1][j + 1] + 1;dp[i][j]=dp[i+1][j+1]+1;。再遍历一遍dpdpdp数组,如果满足 dp[i][j]≥j−idp[i][j] \ge j-idp[i][j]≥j−i,说明以 iii 开头往后的 2×(j−i)2 \times (j - i)2×(j−i)子串符合题意。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
string s;
cin >> s;
int n = s.size();
s = " " + s;
vector<vector<int>> dp(n + 2, vector<int>(n + 2));
for (int i = n; i >= 1; i--) {
for (int j = n; j >= 1; j--) {
if (s[i] == '?' || s[j] == '?' || s[i] == s[j]) {
dp[i][j] = dp[i + 1][j + 1] + 1;
}
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if (dp[i][j] >= j - i) {
ans = max(ans, (j - i) * 2);
}
}
}
cout << ans << endl;
}
return 0;
}
E.Clique Partition (图论)
题意:
给你两个整数 nnn 和 kkk 。在 nnn 个顶点上有一个图,编号从 111 到 nnn ,最初没有边。
现在要为每个顶点分配一个整数;让 aia_iai 成为顶点 iii 上的整数。所有的 aia_iai 都应该是从 111 到 nnn 的不同整数。
分配整数后,对于每一对顶点 (i,j)(i, j)(i,j) ,如果有 ∣i−j∣+∣ai−aj∣≤k|i - j| + |a_i - a_j| \le k∣i−j∣+∣ai−aj∣≤k 则在它们之间添加一条边。
现在的目标是创建一个可以分割成尽可能少的(对于给定的 nnn 和 kkk 值)小群的图。图中的每个顶点都应属于一个小群。一个小群是一个顶点集合,其中的每一对顶点都有一条边相连。
分析:
通过观察发现,我们需要选择一个连续的区间作为一个小群,因为连续的区间∣i−j∣\vert i-j \vert∣i−j∣是最小的。通过打表发现对于长度为xxx的区间,∣i−j∣+∣ai−aj∣\vert i-j \vert + \vert a_i-a_j \vert∣i−j∣+∣ai−aj∣同样是xxx。接下来就可以按顺序排列,然后把一半的前缀放到后面去来进行构造。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n, k;
cin >> n >> k;
vector<int> ans;
vector<int> color;
int tmp = min(k, n);
int cnt = 0;
for (int i = 1; i <= n; i += tmp) {
int l = i, r = min(n, i + tmp - 1);
++cnt;
vector<int> vis;
for (int j = l; j <= r; j++) {
ans.push_back(cnt);
vis.push_back(j);
}
rotate(vis.begin(), vis.begin() + vis.size() / 2, vis.end());
color.insert(color.end(), vis.begin(), vis.end());
}
for (auto x: color)
cout << x << " ";
cout << endl;
cout << cnt << endl;
for (auto x: ans)
cout << x << " ";
cout << endl;
}
return 0;
}
F.Rare Coins (数学)
题意:
有 nnn 个袋子,编号从 111 到 nnn , iii 个袋子里有 aia_iai 枚金币和 bib_ibi 枚银币。
一枚金币的价值是 111 。一枚银币的价值是 000 或 111 ,由每枚银币独立决定( 000 的概率是 12\frac{1}{2}21 , 111 的概率是 12\frac{1}{2}21 )。
回答 qqq 个独立的问题。每个问题如下:
- lll rrr - 计算 lll 至 rrr 袋中硬币的总价值严格大于所有其他袋中硬币总价值的概率。
分析:
计算概率实际上是计算有多少种银币的方案数使得自己更大。假设自己有xxx枚银币变成 111,其他有yyy枚变成111。得到下列式子:自己的aaa金币+x>x >x>其他袋中的aaa金币+yyy。即设定一个约束x−y>zx - y > zx−y>z,满足这个约束下的方案数为(b1x)\tbinom{b_1}{x}(xb1) (b2y)\tbinom{b_2}{y}(yb2)
b1,b2b_1 , b_2b1,b2分别表示自己有的银币和其他袋子里的银币。上述式子在形式上和范德蒙德卷积类似,只需要将x−yx - yx−y改成x+yx + yx+y即可。我们修改假设为,有yyy枚变成000,约束变为a1+x>a2+b1−ya_1 + x > a_2 + b_1 - ya1+x>a2+b1−y,a1,a2a_1 , a_2a1,a2和b1,b2b_1, b_2b1,b2定义类似。再由由范德蒙德卷积可知总方案数为(nz+1)+…(nn)\tbinom{n}{z+1}+ \dots \tbinom{n}{n}(z+1n)+…(nn)。nnn表示银币的总数。预处理后缀和之后即可进行计算。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 998244353;
const int N = 1e6 + 5;
LL binpow(LL a, LL n) {
LL ans = 1;
while (n) {
if (n & 1)
ans = (ans * a) % mod;
a = (a * a) % mod;
n >>= 1;
}
return ans;
}
LL inv(LL x) { return binpow(x, mod - 2); }
int a[N], b[N];
int prea[N], preb[N];
LL com[N], suf[N], fact[N], invfact[N];
int main() {
ios::sync_with_stdio(false);
int n, q, l, r;
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
prea[i] = prea[i - 1] + a[i];
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
preb[i] = preb[i - 1] + b[i];
}
com[0] = 1;
fact[0] = 1;
for (int i = 1; i <= preb[n]; i++)
fact[i] = (fact[i - 1] * i) % mod;
invfact[preb[n]] = inv(fact[preb[n]]);
for (int i = preb[n] - 1; i >= 0; i--)
invfact[i] = (invfact[i + 1] * (LL) (i + 1)) % mod;
for (int i = 1; i <= preb[n]; i++)
com[i] = fact[preb[n]] * invfact[preb[n] - i] % mod * invfact[i] % mod;
suf[preb[n]] = com[preb[n]];
for (int i = preb[n] - 1; i >= 0; i--)
suf[i] = (suf[i + 1] + com[i]) % mod;
LL invs = inv(binpow(2, preb[n]));
for (int i = 1; i <= q; i++) {
cin >> l >> r;
int ina = prea[r] - prea[l - 1], inb = preb[r] - preb[l - 1];
int outa = prea[n] - ina, outb = preb[n] - inb;
int condition = max(outa + outb - ina, -1);
LL ans = (condition >= preb[n] ? 0 : suf[condition + 1]);
ans = (ans * invs) % mod;
cout << ans << " ";
}
cout << endl;
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

406

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



