打了差不多一年的cf发现自己是div3选手,也可能是div4+选手。
A
这一题就是简单的模拟题。
#include <iostream>
using namespace std;
long long main() {
long long n, k;
cin >> n >> k;
for(long long i = 0; i < k; i++) {
if(n % 10 == 0) {
n /= 10;
} else {
n--;
}
}
cout << n << endl;
return 0;
}
B
这题是说要算一下哪个长度为2的子串出现次数最多,那么就统计一下吧。
#include <iostream>
using namespace std;
const int maxn = 30;
int cnt[maxn][maxn];
char ss[110];
int main() {
int n;
char c;
scanf("%d", &n);
scanf("%c", &c);
scanf("%s", ss);
for(int i = 0; i < n - 1; i++) {
cnt[ss[i]-'A'][ss[i+1]-'A']++;
}
int mx = -1;
int ii = -1;
int ij = -1;
for(int i = 0; i < 26; i++) {
for(int j = 0; j < 26; j++) {
if(mx < cnt[i][j]) {
ii = i;
ij = j;
mx = cnt[i][j];
}
}
}
printf("%c%c", ii+'A', ij+'A');
return 0;
}
C
这题的主要问题在于,可能有一点点小的细节。
另外还有一个问题是,如果不认真看题,可能会以为要找出一个严格大于k个数的数出来,而不是大于等于,至少我是这么看错的。看错的原因是,给的样例恰好是大于的。具体的细节请看代码。主要是要特判k==0。
代码写的有点蠢,但是还是过了。
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 2e5 + 10;
const int inf = 0x3f3f3f3f;
int a[maxn];
int main() {
int n, k;
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
a[n+1] = inf;
sort(a + 1, a + n + 1);
if(k == 0) {
if(a[1] > 1) {
printf("1");
} else {
printf("-1");
}
return 0;
}
int cnt = 0;
int t = a[k];
int index = k;
int tem = k;
for(int i = k + 1; i <= n; i++) {
if(a[i] <= t) {
index++;
} else {
break;
}
}
if(index == k) {
printf("%d", a[k]);
} else {
printf("-1");
}
return 0;
}
D
这题我感觉我写了一个假算法啊,但是不知道为什么能pp。结果最后还是过了。。不是很懂
大概思路是,先预处理出一个后面可以接哪一个。b数组表示后面接哪个,c数组表示前面接哪个。
后来请教了一下,发现我这个算法没有问题。
事情是这样的,可以发现,对于这堆数,每个数都可以表示为a∗2x∗3ya∗2x∗3y ,只有x和y是变化的,而x变大,y变小,所以应该是一个双关键字排序,至于这个排序怎么排,也是有讲究的,比如说你真的算出x和y,然后排序,这个可以,但是实际上算出一个就够了,因为因数分解下来的那个a是一样的。由于题目保证了有解,所以我这里的做法,就恰好也保证了排序。
#include <iostream>
#include <vector>
using namespace std;
int n;
long long a[110];
int b[110];
int c[110];
int main() {
long long n;
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(a[i] * 3 == a[j]) {
b[j] = i;
c[i] = j;
} else if(a[i] * 2 == a[j]) {
b[i] = j;
c[j] = i;
}
}
}
int ss = -1;
for(int i = 1; i <= n; i++) {
if(c[i] == 0) {
ss = i;
break;
}
}
while(ss != 0) {
cout << a[ss] << " ";
ss = b[ss];
}
return 0;
}
E
这道题的思路可以是这样的:
- 找出所有的联通块。这里可以用dfs来实现。
- 把判断每个联通块是不是都是合法的。我同学讲说看边数与点数是否相等,这个实现起来应该也不难,应该也是对的吧。还有一种做法是,判断每个点的度是否为2.
#include <stdio.h>
#include <vector>
using namespace std;
const int maxn = 2e5 + 10;
vector<int> v[maxn];
vector<int> g;
bool used[maxn];
void dfs(int x) {
used[x] = true;
g.push_back(x);
for(int i : v[x]) {
if(!used[i]) {
dfs(i);
}
}
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
int x, y;
for(int i = 0; i < m; i++) {
scanf("%d%d", &x, &y);
v[x].push_back(y);
v[y].push_back(x);
}
int cnt = 0;
for(int i = 1; i <= n; i++) {
if(used[i]) {
continue;
}
g.clear();
dfs(i);
bool ok = true;
for(int ii : g) {
if(v[ii].size() != 2) {
ok = false;
break;
}
}
if(ok) {
cnt++;
}
}
printf("%d", cnt);
return 0;
}
F
我的想法是这样的:
用dp[x]表示以x为结尾的串的长度,那么如果出现了一个数x,那么这时候更新dp[x],dp[x]=max(dp[x−1]+1,dp[x])dp[x]=max(dp[x−1]+1,dp[x])
。然后有一种像我这样很蠢但是也是很简单易懂的做法是,这时候已经知道子串的所有元素信息了,那么在原来的串中找出来就行了。子串的第一个元素是x−dp[x]+1x−dp[x]+1 ,然后依次在数列中找出大一个的数。
#include <stdio.h>
#include <map>
using namespace std;
const int maxn = 2e5 + 10;
map<int, int> dp;
int a[maxn];
int main() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
dp[a[i]] = max(dp[a[i]], dp[a[i]-1] + 1);
}
int mx_cnt = -1;
int mi = -1;
for(auto a : dp) {
if(a.second > mx_cnt) {
mx_cnt = a.second;
mi = a.first;
}
}
int cnt = mx_cnt;
printf("%d\n", cnt);
int num = mi - cnt + 1;
for(int i = 1; i <= n; i++) {
if(a[i] == num) {
printf("%d ", i);
num++;
}
}
return 0;
}