来自一名div3+选手的题解,非完全版。
A Links and Pearls
这题本质上是一个数学问题。
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
cin >> s;
int cnt1 = 0;
int cnt2 = 0;
for(int i = 0; i < s.length(); i++) {
if(s[i] == '-') {
cnt1++;
} else {
cnt2++;
}
}
if(cnt2 == 0) {
printf("YES");
return 0;
}
if(cnt1 % cnt2 == 0) {
printf("YES");
} else {
printf("NO");
}
return 0;
}
B Marlin
这题应该就是要构造一个对称的情况。我读错题目了,是说如果道路相邻,那么可以通过,而不是,所有的旅馆要连着。
那么按k的奇数偶数来排一下。如果是偶数,那么在(2,2), (2, n-1)开始一直从两边往中间摆。如果第2行摆满了,那么就摆第三行。然后要特判k取 2×(n−2) 2 × ( n − 2 ) 的情况,这时候两个中间要填的。如果是奇数,那么就填一个中间的。
当然我自己不是这样写的,我的写法是,奇数是从中间往两边填,如果还没填够再从两边往中间填,但是偶数的情况下,直接第2行,第3行,都从第2个开始,往右边填。我这种写法是因为,我读错题目了。但是我这种做法确实是对的。首先偶数的情况,这是根据第二行与第三行的中间一行为对称轴的图形,那么显然是可以的。对于奇数的情况,(1,1)->(4, n)的最短路径数量与(1,n)->(4,1)的最短路径数量是一样的。然后(4,1)->(1,n)的最短路径数量与(1,n)->(4,1)的最短路径数量也是一样的。
#include <stdio.h>
#include <iostream>
using namespace std;
int n, k;
const int maxn = 110;
char a[maxn][maxn * 2];
int main() {
scanf("%d%d", &n, &k);
int max = (n - 2) * 2;
if(k > max) {
printf("NO");
return 0;
}
for(int i = 0; i < 4; i++) {
for(int j = 0; j < n; j++) {
a[i][j] = '.';
}
}
if(k % 2 == 0) {
for(int i = 1; i <= k / 2; i++) {
a[1][i] = '#';
a[2][i] = '#';
}
} else {
a[1][n/2] = '#';
k--;
int i1 = n / 2 - 1;
int i2 = n / 2 + 1;
while(i1 > 0 && k > 0) {
a[1][i1] = '#';
a[1][i2] = '#';
i1--;
i2++;
k -= 2;
}
i1 = 1;
i2 = n - 2;
while(k > 0) {
a[2][i1] = '#';
a[2][i2] = '#';
i1++;
i2--;
k -= 2;
}
}
printf("YES\n");
for(int i = 0; i < 4; i++) {
for(int j = 0; j < n; j++) {
printf("%c", a[i][j]);
}
printf("\n");
}
return 0;
}
C Posterized
这个讲究的就是一个贪心。具体的实现可能稍微有点复杂。对于每一个数,我们要从当前数字出发,看能不能放最小值为这个数的组里面,一直到可能的最小值,不能把这个过程反过来。然后把可以放的组的最小值到当前数字,都放入组中。
#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn];
int b[maxn];
int main() {
int n, k;
scanf("%d%d", &n, &k);
memset(b, -1, sizeof b);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++) {
int x = a[i];
if(b[x] != -1) {
continue;
}
int s = x - k + 1;
s = max(0, s);
int t = x;
for(int j = x; j >= s; j--) {
if(b[j] == -1) {
t = j;
} else if(x - b[j] + 1 <= k) {
t = b[j];
break;
} else {
break;
}
}
for(int j = t; j <= x; j++) {
b[j] = t;
}
}
for(int i = 1; i <= n; i++) {
printf("%d ", b[a[i]]);
}
return 0;
}
D Perfect Groups
这题主要是要能读懂题目。
首先介绍了一个性质f,使得一个数组A对应的映射值为t,那么有f(A)=t。对于给定的数组 B B ,有若干种取其连续子序列的做法,使得得到 ,对于每个 Bx B x ,有一个 f(Bx)=tx f ( B x ) = t x ,要求统计 tx t x 各出现了多少次。
对于这里的性质,我在代码的注释里面有详细的讲解。
#include<cstdio>
#include<map>
using namespace std;
#define ran 5555
int n;
int a[ran], s[ran], pre[ran];
map<int, int> mp;
int main() {
// 获取输入并预处理输入
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
int f = (a[i] < 0) ? -1 : 1;
a[i] *= f;
// 如果一个数可以分解为a*b^x*... 其中x>=2,那么这时候x可以变成x-2,因为这个数a1与别的数a2相乘的时候如果为平方数,那么这个数a1除以一个平方数与a2相乘仍然是平方数
// 所以这时候,实际上要统计的就是,在处理了之后,子序列中有多少个不一样的数,就要分成多少个组,对于0的情况下面有特判
for (int j = 2; j * j <= a[i]; j++) {
while (a[i] % (j * j) == 0) {
a[i] /= j * j;
}
}
a[i] *= f;
}
// 找出相邻的在i号数前面的一样的数的序号
mp.clear();
for (int i = 1; i <= n; i++) {
pre[i] = mp[a[i]];
mp[a[i]] = i;
}
// i表示连续子序列的开始位置(包括)
for (int i = 1; i <= n; i++) {
// cur表示除了0以外有多少个不同的数
int cur = 0;
// j表示连续子序列的结束位置(包括)
for (int j = i; j <= n; j++) {
// a[j] != 0表示,计算有多少个不同的数的时候,并不在意有没有0,因为0放哪个组里面都行。而如果只有0的话,下面的s[max(1, cur)]++;已经处理好了这种情况
// pre[j] < i 表示,第j号元素前一次出现是在i之前,也就是说,在这里是第一次出现,也就是说,多出现了一个不同的数
if (a[j] != 0 && pre[j] < i) {
cur++;
}
// 如果有0个数,那么实际上也是要分成1组
s[max(1, cur)]++;
}
}
// 输出
for (int i = 1; i <= n; i++) {
printf("%d%c", s[i], " \n"[i >= n]);
}
return 0;
}
E The Number Games
据说用线段树或者树状数组都可以,但是我看了题解还是不能理解到底是怎么实现的,以后有机会补一补吧。

9389

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



