比赛链接

A题

先求前缀和,如果当前的和可以拼成一个完整的正方形,可以发现它必须是奇数$2k + 1,k\in N^{+}$的平方,直接判断即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
std::set<int> ms;
void solve() {
int n;
cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
if (i) {
a[i] += a[i - 1];
}
}
int ans = 0;
for (int i = 0; i < n; i++) {
if (ms.find(a[i]) != ms.end()) {
ans++;
}
}
cout << ans << '\n';
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
for (int i = 1; i <= 101; i += 2) {
ms.insert(i * i);
}
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}

B题

对于一个长度为$n$的字符串,他可组成的排列数是$\frac{n!}{n_{a}!n_{b}!…n_{z}!}$,我们要改变一个字母,使得这个值最小,也就是使得分母最大,那么只需要将一个数量最少的字母变为一个数量最多的字母,这时相当于把原来的排列方法总数乘$\frac{n_{min}}{n_{max}+1}$,易得没有比这个更小的值了。

需要特别注意的是如果字符串由两个及以上的数量相等的字符组成,我们的程序要能选择两个不同的字符并将其中一个变为另一个,而不是对相同字符做操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void solve() {
int n;
cin >> n;
string s;
cin >> s;
std::vector<int> cnt(26);
for (char ch : s) {
cnt[ch - 'a']++;
}
char minc, maxc;
int mn = INF, mx = -1;
for (int i = 0; i < 26; i++) {
if (cnt[i] == 0) continue;
if (mn >= cnt[i]) {
mn = cnt[i];
minc = i + 'a';
}
if (mx < cnt[i]) {
mx = cnt[i];
maxc = i + 'a';
}
}
for (int i = 0; i < n; i++) {
if (s[i] == minc) {
s[i] = maxc;
cout << s << '\n';
return;
}
}
}

C题

由于只可以选择当前格子右侧和下侧的格子,那么就肯定有一列两个数都被选中,而其余列我们都能取到最大值(通过交换),枚举作为转弯处的列即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
template<typename T>
void chmax(T& a, T b){
if (a < b) {
a = b;
}
}
void solve() {
int n;
cin >> n;
std::vector<int> sum(n), mx(n);
for (int i = 0; i < n; i++) {
int num;
cin >> num;
sum[i] = num;
mx[i] = num;
}
int mxsum = 0;
for (int i = 0; i < n; i++) {
int num;
cin >> num;
sum[i] += num;
chmax(mx[i], num);
mxsum += mx[i];
}
int ans = -INF;
for (int i = 0; i < n; i++) {
chmax(ans, mxsum - mx[i] + sum[i]);
}
cout << ans << '\n';
}

D题

贪心思想,先从尾到头求一个最小栈,以便于判断当前数字是否是之后所有数字中最小的,如果是,那么它一定不需要进行操作,就可以加入到答案序列中;如果不是,则说明它之后还有比它更小的数字,那么它一定需要操作,将其加一之后移到最后,假如这个数是$a$,在它移到最后时就是$a+1$,此时我们又多了个判断条件:因为这个$a+1$是一定会被排到原序列最后面,所以在以后的数字不仅要判断它是否是原序列之后所有数字最小的,还应是所有需要操作移到后面去的数字中最小的,否则我们仍然可以把它移到最后面,而使$a_{min}+1$移动到该位置,使得最后的序列更小。

事实上,我们可以将所有需要移动的数字用一个数组存储起来,使用一个值来随时记录更新数组中最小的数字,这样我们就可以随时知道$a_{min}+1$,并且之后排序,按从小到大的顺序依次将数组中的元素加一输出,加一后放到序列末尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
inline void chmin(T& a, T b){
if (a > b) {
a = b;
}
}
void solve() {
int n;
cin >> n;
std::vector<int> a(n), minstack(n), ans;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = n - 1; i >= 0; i--) {
if (i == n - 1) {
minstack[i] = a[i];
} else {
minstack[i] = std::min(minstack[i + 1], a[i]);
}
}
std::vector<int> tmp;
int mn = INF, mn1 = INF;
for (int i = 0; i < n; i++) {
if (a[i] == minstack[i]) {
if (a[i] <= mn1) {
cout << a[i] << ' ';
} else {
tmp.push_back(a[i]);
}
} else {
tmp.push_back(a[i]);
chmin(mn1, a[i] + 1);
}
}
std::sort(tmp.begin(), tmp.end());
for (int i : tmp) {
cout << i + 1 << ' ';
}
cout << '\n';
}