A.Count Takahashi
题意
给出NNN个字符串,每个字符串为以下两种字符串之一:
-
"Takahashi" -
"Aoki"
请你统计"Takahashi"出现了多少次。
分析
输入并统计即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
int n;
cin >> n;
int ans = 0;
for (int i = 0; i < n; i++) {
string s;
cin >> s;
if (s[0] == 'T') ans++;
}
cout << ans << endl;
}
int main() {
solve();
return 0;
}
B. Couples
题意
有NNN对人,每队人身上都穿着相同颜色的衣服,保证每种颜色只会出现两次(即只有同一对人才能拥有相同颜色)。
问:存在多少个人,同时与两个相同颜色衣服的人相邻?
分析
输入后依次判断是否存在第iii个人和第i−2i - 2i−2个人衣服颜色相同即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5e2;
int a[N];
void solve() {
int n;
cin >> n;
int ans = 0;
for (int i = 1; i <= 2 * n; i++) {
cin >> a[i];
if (i >= 3 && a[i] == a[i - 2]) ans++;
}
cout << ans << endl;
}
int main() {
solve();
return 0;
}
C.Tile Distance 2(思维)
题意
有一个由大小为2×12 \times 12×1的瓷砖铺成的平面,其中使用(x,y)(x, y)(x,y)表示位于坐标(x+0.5,y+0.5)(x + 0.5, y + 0.5)(x+0.5,y+0.5)的网格所在的位置。

问,从瓷砖(Sx,Sy)(S_{x}, S_{y})(Sx,Sy)走到瓷砖(Tx,Ty)(T_{x}, T{y})(Tx,Ty)最少仅需要经过多少个瓷砖(不包含起点)?
分析
不难发现,每块瓷砖的左半部分的x,yx, yx,y坐标之和必然为偶数,右半部分的x,yx, yx,y坐标之和必然为奇数。因此,为了便于处理,可以将输入的两个坐标均移动到瓷砖的左半边,此时不需要经过其他瓷砖。
然后考虑怎么移动最优,可以想到,每次向上或向下移动后,均可以再横向移动一次,此时也不需要经过其他瓷砖,即一次移动可以使两个瓷砖的行列坐标均接近111,那么只需要经过min(∣Sx−Tx∣,∣Sy−Ty∣)min(|S_x - T_x|, |S_y - T_y|)min(∣Sx−Tx∣,∣Sy−Ty∣)次操作,就能保证其中一个坐标相等了。
然后考虑此时的两种情况:
-
xxx坐标不相等,需要当前的xxx坐标之差除以二的操作次数
-
yyy坐标不相等,需要当前的yyy坐标之差的操作次数
令X,YX, YX,Y为两个点的x,yx, yx,y坐标之差的绝对值。
对于情况1,不难发现每次操作均能使两个点的曼哈顿距离接近222,共需X+Y2\frac{X + Y}{2}2X+Y次操作。
对于情况2,不难发现需要的操作次数为YYY,为便于计算,将操作次数变为Y+Y2\frac{Y + Y}{2}2Y+Y。
两式结合,操作次数变为max(X,Y)+Y2\frac{max(X, Y) + Y}{2}2max(X,Y)+Y,按公式计算输出即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5e2;
int a[N];
void solve() {
ll sx, sy, tx, ty;
cin >> sx >> sy >> tx >> ty;
if (sx + sy & 1) sx--;
if (tx + ty & 1) tx--;
ll x = abs(sx - tx);
ll y = abs(sy - ty);
ll ans = (y + max(x, y)) / 2;
cout << ans << endl;
}
int main() {
solve();
return 0;
}
D.Avoid K Palindrome(动态规划)
题意:
给你一个长度为NNN的字符串SSS,由字符A、B和?组成。
同时给你一个正整数KKK。如果满足以下条件,那么由A和B组成的字符串TTT就是一个好字符串:
- 在TTT中,长度为KKK的连续子串都不是回文。
设qqq是SSS中的?字符数量,用A或B替换SSS中的每个?,可以得到2q2^q2q个字符串。请找出其中有多少个字符串是好字符串。
这个数目可能非常大,答案对998244353998244353998244353取模。
分析:
从左到右考虑每个字符,如果当前是?,则考虑其变为AAA,BBB,是否出现长度为kkk的回文串。
我们需要知道该?前k−1k−1k−1位的情况,加上该字母,就可以判断出新增的子串是不是回文串。
即设dpi,jdp_{i,j}dpi,j表示考虑前iii位字符,其中?都已经替换成AAA或BBB后,且后999位的字符状态为jjj(因为只有ABABAB两种,可以编码成010101,用二进制压缩表示)。
然后考虑当前位的情况,如果取值为AAA即000,则判断j<<1j<<1j<<1状态是不是回文串,不是的话则有dpi+1,(j<<1)&mask+=dpi,jdp_{i+1,(j<<1) \&mask}+=dp_{i,j}dpi+1,(j<<1)&mask+=dpi,j,否则就状态非法,不转移。因为j<<1j<<1j<<1是后101010个字符的状态信息,而jjj的含义是后999位,所以&mask\&mask&mask是把第101010位去掉。
同理,取值为BBB的话,即111,则判断(j<<1)∣1(j<<1)|1(j<<1)∣1是不是回文串,不是的话就转移,否则不转移。事先预处理每个状态是否是回文串,然后当i≥ki≥ki≥k时再考虑转移的合法性。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MOD = 998244353;
int main() {
int n, k;
string s;
cin >> n >> k >> s;
int up = (1 << k);
vector<int> p(up);
for (int i = 0; i < up; i++) {
vector<int> bit(k);
int num = i;
for (int j = 0; j < k; j++) {
bit[j] = (num & 1);
num >>= 1;
}
auto rev = bit;
reverse(rev.begin(), rev.end());
p[i] = rev == bit;
}
up = 1 << (k - 1);
int mask = up - 1;
vector<int> dp(up, 0);
dp[0] = 1;
for (int i = 0; i < n; ++i) {
int chr = s[i];
vector<int> dp2(up, 0);
for (int j = 0; j < up; j++) {
if (chr == '?') {
if (i + 1 < k || !p[j << 1]) {
int nxt = (j << 1) & mask;
dp2[nxt] = (dp2[nxt] + dp[j]) % MOD;
}
if (i + 1 < k || !p[j << 1 | 1]) {
int nxt = (j << 1 | 1) & mask;
dp2[nxt] = (dp2[nxt] + dp[j]) % MOD;
}
} else {
if (i + 1 < k || !p[j << 1 | (chr - 'A')]) {
int nxt = (j << 1 | (chr - 'A')) & mask;
dp2[nxt] = (dp2[nxt] + dp[j]) % MOD;
}
}
}
dp.swap(dp2);
}
LL ans = 0;
for (int i = 0; i < up; i++) {
ans = (ans + dp[i]) % MOD;
}
cout << ans << endl;
return 0;
}
E.Water Tank(单调栈)
题意:
给你一个长度为NNN的正整数序列:H=(H1,H2,…,HN)H=(H_1,H_2,\dotsc,H_N)H=(H1,H2,…,HN)。
有一个长度为N+1N+1N+1的非负整数序列:A=(A0,A1,…,AN)A=(A_0,A_1,\dotsc,A_N)A=(A0,A1,…,AN)。最初为A0=A1=⋯=AN=0A_0=A_1=\dotsb=A_N=0A0=A1=⋯=AN=0。
对AAA重复进行以下运算:
- 将A0A_0A0的值增加111。
- 依次对i=1,2,…,Ni=1,2,\ldots,Ni=1,2,…,N进行以下操作:
- 如果Ai−1>AiA_{i-1}\gt A_iAi−1>Ai和Ai−1>HiA_{i-1}\gt H_iAi−1>Hi,则将Ai−1A_{i-1}Ai−1的值减少111,并将AiA_iAi的值增加111。
求每个i=1,2,…,Ni=1,2,\ldots,Ni=1,2,…,N在Ai>0A_i\gt 0Ai>0第一次成立之前的运算次数。
分析:
观察样例,模拟一下,很容易发现如果要求第iii位的答案,则要求之前的第i−1i−1i−1位装满,即和柱子aia_iai高度一样,而高度一样要求i−2,i−3,…i−2,i−3,\dotsi−2,i−3,…位高度也一样。观察上述会发现,假设前面比柱子aia_iai还高的柱子是aja_jaj,那么j,j+1,…,i−1j,j+1,\dots,i−1j,j+1,…,i−1位的水高都必须是aia_iai。
因此,求解第iii位的答案,则需要i−1,i−2,…,ji−1,i−2,\dots,ji−1,i−2,…,j位与aia_iai高度相同,然后j−1,j−2,…,kj−1,j−2,\dots,kj−1,j−2,…,k与aja_jaj高度相同,k−1,k−2,…k−1,k−2,\dotsk−1,k−2,…与aka_kak高度相同,其中ai≤aj≤aka_i≤a_j≤a_kai≤aj≤ak,需要维护一个从左到右,柱子高度单调递减的序列,可以用单调栈进行维护,栈底是最左边,栈顶是最右边。在维护递减高度时,同时维护每个递减区间的水高度总和即可。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MOD = 998244353;
int n;
int main() {
cin >> n;
LL ans = 1;
vector<pair<int, int>> f;
f.emplace_back(0, INT_MAX);
for (int i = 1, x; i <= n; i++) {
cin >> x;
int lst, lx;
while (f.back().second <= x) {
lst = f.back().first;
lx = f.back().second;
f.pop_back();
ans -= 1ll * (lst - f.back().first) * lx;
}
ans += 1ll * x * (i - f.back().first);
f.emplace_back(i, x);
cout << ans << " ";
}
cout << endl;
return 0;
}
F.Tree Degree Optimization(树、优先队列)
题意:
问题陈述
给你一个整数序列A=(A1,…,AN)A=(A_1,\ldots,A_N)A=(A1,…,AN)。对于一棵有NNN个顶点的树TTT,定义f(T)f(T)f(T)如下:
- 设did_idi是TTT中顶点iii的度数。那么,f(T)=∑i=1Ndi2Aif(T)=\sum\limits_{i=1}^N{d_i}^2 A_if(T)=i=1∑Ndi2Ai。
求f(T)f(T)f(T)的最小可能值。
约束条件保证答案小于2632^{63}263。
分析:
由于是一棵树,则满足:
- ∀1≤i≤n,di≥1∀1≤i≤n,d_i≥1∀1≤i≤n,di≥1
- ∑di=2n−2,1≤di≤n−1\sum d_i=2n−2,1≤d_i≤n−1∑di=2n−2,1≤di≤n−1。
对于任意满足上述条件的did_idi,都可以构造出对应的树,使得每个点的度数都是did_idi。构造方法为每次选择度数为111和非111的点连边,然后更新剩余度数。
下面考虑如何分配这些度数。
如果给点111分配一个度,是其d1=1→2d_1=1→2d1=1→2,则代价是4a1−a14a_1−a_14a1−a1,而如果是d1=2→3d_1=2→3d1=2→3,则代价是9a1−4a19a_1−4a_19a1−4a1。
我们考虑贪心的做法,直接先把全部did_idi赋上111的权值,然后把剩下的n−2n−2n−2个度分配给每个点,使得代价最小,每次仅分配111的度,贪心的将其分配给代价最小的点。用优先队列维护上述代价即可。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 200005;
const int MOD = 998244353;
int n, a[N], d[N];
LL ans;
priority_queue<pair<LL, int>, vector<pair<LL, int> >, greater<pair<LL, int> > > q;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
ans += a[i];
d[i] = 1;
q.emplace(3ll * a[i], i);
}
for (int i = 1; i <= n - 2; i++) {
int id = q.top().second;
ans += q.top().first;
q.pop();
d[id]++;
q.emplace(a[id] * (d[id] * 2ll + 1), id);
}
cout << ans << endl;
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

1122

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



