题目列表链接:http://codeforces.com/contest/1203
A Circle of Students
题意:现在有 n n n个人标号为 1 − n 1-n 1−n站成一个圈,以圈中的随意一个人为起始按照顺时针站成升序,或者降序。现在给你一个圈(起点未确定),问是否符合以上说的站位标准。
解题心得:直接判断一下数列是否满足升序或者降序并且其中至多只有一个突变(即不符合顺序条件的位置)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
int num[maxn], n;
bool checke_up() {
int cnt = 0;
for(int i=0;i<n;i++) {
if(num[i] == num[(i+1)%n] - 1) continue;
cnt++;
}
return cnt < 2;
}
bool checke_down() {
int cnt = 0;
for(int i=0;i<n;i++) {
if(num[i] == num[(i+1)%n] + 1) continue;
cnt++;
}
return cnt < 2;
}
int main() {
// freopen("1.in.txt", "r", stdin);
int q; scanf("%d", &q);
while(q--) {
scanf("%d", &n);
for(int i=0;i<n;i++) scanf("%d", &num[i]);
bool flag = false;
flag |= checke_up();//检验升序
flag |= checke_down();//检验降序
if(flag) puts("YES");
else puts("NO");
}
return 0;
}
B Equal Rectangles
题意:现在有 4 ∗ n 4*n 4∗n根木棍,问是否可以组成 n n n个矩形,并且矩形的面积相等。
解题心得:直接将木棍排序,然后分别从首位各取两根,判断长度、计算面积是否符合就行了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
int num[maxn], n;
int main() {
// freopen("1.in.txt", "r", stdin);
int q; scanf("%d", &q);
while(q--) {
scanf("%d", &n);
n *= 4;
for(int i=1;i<=n;i++) scanf("%d", &num[i]);
sort(num+1, num+n+1);
int head = 1, tail = n, s = 0;
bool flag = false;
while(head < tail) {
if(num[head] != num[head+1] || num[tail] != num[tail-1]) {
flag = true;
break;
}
if(head == 1) {
s = num[head] * num[tail];
} else {
if(num[head] * num[tail] != s) {
flag = true;
break;
}
}
head += 2;
tail -= 2;
}
if(!flag) puts("YES");
else puts("NO");
}
return 0;
}
C Common Divisors
题意:现在给你一个长度为 n n n的数列,问所有的数有多少个相同的因子。
解题心得:直接找到这 n n n个数字的公共 g c d gcd gcd,然后将 g c d gcd gcd的因子全部找出来,数一下个数,没了。(一个数的因子的因子也是这个数的因子)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e5+100;
ll num[maxn], n;
int main() {
// freopen("1.in.txt", "r", stdin);
scanf("%lld", &n);
for(int i=1;i<=n;i++) scanf("%lld", &num[i]);
sort(num+1, num+n+1);
ll Gcd = num[1];
for(int i=1;i<=n;i++) Gcd = __gcd(Gcd, num[i]);
int ans = 0;
for(ll i=1;i*i<=Gcd;i++) {
if(Gcd%i == 0) {
if(i*i == Gcd) ans++;
else ans += 2;
}
}
printf("%d\n", ans);
return 0;
}
D2 Remove the Substring (hard version)
题意:现在有一个字符串 s s s,以及 s s s中的一个子序列 t t t,现在要求你将 s s s删去最长一段字串,删去之后 t t t还是 s s s的子序列,求删去的最长字串有多长。
解题心得:就三个情况,删去的 s s s的前缀、后缀、中间一段。处理也很容易,尽量在 s s s前面找出 t t t来,尽量在 s s s后面找出 t t t来,并且记录找出的 t t t中每一个字符对应在s中的位置,这个时候删去前缀后缀就直接解决了。中间一段就从枚举从 s s s前面找 x x x个字符后面找 t . l e n g t h − x t.length-x t.length−x个字符,因为之前已经处理了相对的位置,减一下中间就出来了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e5+100;
char s[maxn], t[maxn];
int len1, len2, pos1[maxn], pos2[maxn];
//len1为s的长度,len2为t的长度,pos1记录t在s尽量前端找出来的时候相对每个字母位置,pos2为后段
int pre_m() {//尽量在s前段找出t来
int head1 = 0, head2 = 0;
int cnt = 0;
for(int i=0;i<len1;i++) {
if(s[head1] == t[head2]) {
head2++;
pos1[++cnt] = head1;
}
head1++;
if(head2 == len2) break;
}
return len1 - head1;
}
int tail_m() {//尽量在s后段找出t来
int tail1 = len1-1, tail2 = len2-1;
int cnt = 0;
for(int i=len1-1;i>=0;i--) {
if(s[tail1] == t[tail2]) {
pos2[++cnt] = tail1;
tail2--;
}
tail1--;
if(tail2 == -1) break;
}
return tail1 + 1;
}
int main() {
// freopen("1.in.txt", "r", stdin);
scanf("%s%s", s, t);
len1 = strlen(s);
len2 = strlen(t);
int Max = 0;
Max = max(Max, pre_m());
Max = max(Max, tail_m());
for(int i=1;i<=len2;i++) {//删去中间一段,就用前面和后面找出来的位置拼起来
Max = max(Max, pos2[len2-i] - pos1[i] - 1);
}
printf("%d\n", Max);
return 0;
}
E Boxers
题意:现在有 n n n个数,每个数你可以加一或者减一,但是变化后的数也必须是个正数,问你最多可以把这 n n n个数变成多少个不同的数。
解题心得:先排个序,直接从 1 1 1开始枚举,以上 n n n个数中是否可以用一个数变成我枚举的数,如果可以答案加 1 1 1,没了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
int n, num[maxn];
bool vis[maxn];
int main() {
// freopen("1.in.txt", "r", stdin);
scanf("%d", &n);
int ans = 0;
for(int i=1;i<=n;i++) {
scanf("%d", &num[i]);
}
sort(num+1, num+1+n);
int Index = 1;//枚举数字
for(int i=1;i<=n;i++) {
while(num[i]-1 > Index) Index++;
if(num[i] == Index || num[i]-1 == Index || num[i]+1 == Index) {
ans++;
Index++;
}
}
printf("%d\n", ans);
return 0;
}
F1 Complete the Projects (easy version)
题意:现在你有一个积分 r r r,有 n n n个任务等你完成,每个任务有个积分要求 a a a,完成后你的积分变成 r + b r+b r+b, b b b可以是负数,但是你不能把自身的积分变成负数。现在问你是否可以将任务全部完成。
解题心得:首先把 b b b正的先做,这个时候肯定是对 a a a排序,小的先做,然后一边增加 r r r一边检验是否可以完成所有正的任务。然后对于 b b b为负数的任务按照 a + b a+b a+b降序排序,口糊证明一下吧,假设有两个任务 x x x和 y y y,若 ( x . a + x . b > y . a + y . b ) (x.a+x.b>y.a+y.b) (x.a+x.b>y.a+y.b),这个时候若先完成 x x x,因为有 ( x . a + x . b > y . a + y . b ) (x.a+x.b>y.a+y.b) (x.a+x.b>y.a+y.b)且 ( y . b < 0 ) (y.b<0) (y.b<0)可以推出 ( x . a + x . b > y . a ) (x.a+x.b>y.a) (x.a+x.b>y.a),接下来肯定可以继续完成任务 y y y。排序之后直接跟着顺序写就行了。这个题就是看你是否可以找到贪心策略,找不到的话跟着感觉猜嘛,该是没问题的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e7+100;
int n, m;
struct Node {
int a, b;
}node[maxn];
bool cmp(Node x, Node y) {
return x.a < y.a;
}
bool cmp2(Node x, Node y) {
return x.a + x.b > y.a + y.b;
}
int main() {
// freopen("1.in.txt", "r", stdin);
scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++) {
scanf("%d%d", &node[i].a, &node[i].b);
}
sort(node+1, node+1+n, cmp);
int ans = 0;
for(int i=1;i<=n;i++) {
if(node[i].b <= 0) continue;
if(node[i].a <= m) {
m += node[i].b;
ans++;
} else {
printf("NO");
return 0;
}
}
sort(node+1, node+1+n, cmp2);
for(int i=1;i<=n;i++) {
if(node[i].b > 0) continue;
if(m >= node[i].a) {
m += node[i].b;
} else {
puts("NO");
return 0;
}
}
if(m >= 0) puts("YES");
else puts("NO");
return 0;
}
F2 Complete the Projects (hard version)
题意:如F1只不过问题变成了最多可以完成多少个任务。
解题心得:贪心策略还是跟着F1走,然后问个数其实就是一个稍有变化的0-1背包,出题人还很良心的给了一个比较复杂的样例,可能是觉得题太简单吧连逗弄选手的心情都没有。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e7+100;
int n, m;
struct Node {
int a, b;
}node[maxn];
bool cmp(Node x, Node y) {
return x.a < y.a;
}
bool cmp2(Node x, Node y) {
return x.a + x.b > y.a + y.b;
}
int dp[maxn];
int main() {
// freopen("1.in.txt", "r", stdin);
scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++) {
scanf("%d%d", &node[i].a, &node[i].b);
}
sort(node+1, node+1+n, cmp);
int ans = 0;
for(int i=1;i<=n;i++) {
if(node[i].b < 0) continue;
if(node[i].a <= m) {
m += node[i].b;
ans++;
} else {
break;
}
}
int Max = 0;
sort(node+1, node+1+n, cmp2);
for(int i=1;i<=n;i++) {
if(node[i].b >= 0) continue;
for(int j=node[i].a;j<=m;j++) {
if(j + node[i].b >= 0) {
dp[j+node[i].b] = max(dp[j+node[i].b], dp[j]+1);
Max = max(Max, dp[j+node[i].b]);
}
}
}
printf("%d\n", ans + Max);
return 0;
}
比赛心得:
最近打多校被大佬们虐的有点怀疑人生,打一场cf3来找找自我,但是这个cf3也太简单了吧,具体考的算法,嗯,排序吧。把题做完之后还有三四十分钟,体会了一把(虚假)大佬的感觉,不说了,多校补题去了,继续怀疑人生。