→ 比赛传送门 ←
A | B | C | D | E | F | G | H | I | J | K | L |
---|---|---|---|---|---|---|---|---|---|---|---|
○ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
√ 表示已完成,○表示待补
文章目录
B 小宝的幸运数组
题意
定义一个和能被幸运数字整除的数组被称为“幸运数组”
现给幸运数字
k
k
k 和长度为
n
n
n 的数组
a
a
a,求最长的幸运子数组。
思路
首先求出数组 a a a 的前缀和数组,然后对前缀和数组的各位对 k k k 取模运算。先记录各种模数最先出现的位置,再找最后出现的相同模数的位置并维护幸运子数组长度的最大值。
Accepted code
/*
* @Autor: CofDoria
* @Date: 2021-01-30 14:51:53
* @LastEditTime: 2021-01-31 13:01:01
*/
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
int t;
long long n, k;
long long a[100005];
long long pre[100005];
long long ans;
int main() {
a[0] = 0;
scanf("%d", &t);
while (t--) {
ans = -1;
scanf("%lld%lld", &n, &k);
memset(pre, -1, sizeof(pre));
pre[0] = 0;
for (long long i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
a[i] += a[i - 1];
long long idx = a[i] % k;
if (pre[idx] == -1)
pre[idx] = i;
else
ans = max(ans, i - pre[idx]);
}
printf("%lld\n", ans);
}
}
C 上进的凡凡
题意
求数组中非递减子数组的个数。
思路
遍历数组,每当当前元素不小于前一元素,给定数组的非递减子数组的个数都会增加,增加的个数和以当前元素为结尾的最长非递减子数组的长度的相同。
Accepted code
#include <cstdio>
using namespace std;
long long n;
long long ans = 0;
long long a, la = -1;
int main() {
scanf("%lld", &n);
for (long long i = 0, cnt = 0; i < n; i++) {
scanf("%lld", &a);
if (a >= la)
++cnt;
else
cnt = 1;
ans += cnt;
la = a;
}
printf("%lld", ans);
}
D Seek the Joker I
题意
有一堆
n
n
n 张牌构成的牌堆,每人最多抽
k
k
k 张牌,最少抽
1
1
1 张,该牌堆的最后一张作为乌龟,抽中的人输掉比赛,假如两位玩家绝顶聪明,先手者输则输出ma la se mi no.1!
,否则输出yo xi no forever!
。
思路
巴什博奕
若牌堆共有
(
k
+
1
)
∗
x
+
1
(k + 1)*x+1
(k+1)∗x+1 张牌(x为任意非负整数) ,后手玩家都可以根据先手玩家的操作取牌,使两人一回合拿牌数和为
(
k
+
1
)
(k+1)
(k+1),保证除乌龟的最后一张牌为自己取得。该情况下后手必胜。
否则,先手玩家都可以保证除乌龟的最后一张牌为自己取得。该情况下先手必胜。
Accepted code
#include <cstdio>
using namespace std;
int t;
long long n, k;
int main() {
scanf("%d", &t);
while (t--) {
scanf("%lld%lld", &n, &k);
if ((n - 1) % (k + 1) == 0)
printf("ma la se mi no.1!\n");
else
printf("yo xi no forever!\n");
}
}
E Seek the Joker II
题意
有一由
n
n
n 张牌组成的牌堆,第
x
x
x 张为乌龟,取到的人则输掉比赛。
两个人轮流取牌,每次可以从上方或下方取任意张牌,或从上方和下方取相同张数的牌。
假如两位玩家绝顶聪明,先手者输则输出ma la se mi no.1!
,否则输出yo xi no forever!
。
思路
威佐夫博弈
威佐夫博弈公式:
两堆石子为
(
x
,
y
)
(
x
<
y
)
(x,y)(x<y)
(x,y)(x<y),当且仅当
f
l
o
o
r
(
(
y
−
x
)
∗
(
5
+
1
)
2
)
=
x
floor((y-x)*\frac{(\sqrt{5}+1)}{2})=x
floor((y−x)∗2(5+1))=x 时,先手必败,否则先手必胜。
对该题来说,两堆石子就是上下两部分牌堆 ( n − x , x − 1 ) (n-x,x-1) (n−x,x−1),代入公式即可。
Accepted code
/*
* @Autor: CofDoria
* @Date: 2021-01-31 11:09:14
* @LastEditTime: 2021-01-31 12:21:37
*/
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
int t;
long long n, x;
int main() {
scanf("%d", &t);
while (t--) {
scanf("%lld%lld", &n, &x);
long long a = n - x, b = x - 1;
if (a < b) swap(a, b);
if (floor((a - b) * (pow(5.0, 0.5) + 1) / 2) == b)
puts("ma la se mi no.1!");
else
puts("yo xi no forever!");
}
return 0;
}
F 成绩查询ing
题意
依次输入学生的名字、成绩、性别
(
1
/
2
)
(1/2)
(1/2)和学号。
然后输入查询操作 操作
1
1
1 为输入名字查询学生、成绩、性别和学号;操作
2
2
2 为输入成绩按字典序输出学生名字(无该成绩的学生则不输出)。
思路
用结构体储存学生信息,然后重载
<
<
< 符号,按名字的字典序比较结构体的大小。
在输入结束后
s
o
r
t
sort
sort 一遍。
而后对于操作
1
1
1,用
l
o
w
e
r
_
b
o
u
n
d
lower\_bound
lower_bound 函数寻找该名字的学生。对于操作
2
2
2,按顺序寻找该成绩学生,输出结果即是字典序排序。
Accepted code
/*
* @Autor: CofDoria
* @Date: 2021-01-30 12:51:38
* @LastEditTime: 2021-02-04 15:54:15
*/
#include <bits/stdc++.h>
using namespace std;
#define db double
#define ll long long
#define inf 0x3f3f3f3f
#define s(a, n) memset(a, n, sizeof(a))
#define debug(a) cout << '#' << a << '#' << endl
#define rep(l, a, b) for (register ll l = a; l < b; ++l)
#define per(l, a, b) for (register ll l = a; l >= b; --l)
#define _ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define _forit(i, c) \
for (__typeof__((c).begin()) i = (c).begin(); i != (c).end(); ++i)
bool fi = true;
const unsigned long long MOD = 1e9 + 7;
inline ll gcd(ll a, ll b) { return (b == 0 ? a : gcd(b, a % b)); }
int n, m, op;
struct stu {
int gender, grade, no;
string name;
stu(int a = 0, int b = 0, int c = 0, string s = "")
: gender(a), grade(b), no(c), name(s) {}
bool operator<(const stu x) const { return name < x.name; }
} s[100005];
inline bool cmp(stu a, stu b) { return a.grade < b.grade; }
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
scanf("%d", &n);
for (int i = 0; i < n; i++) {
cin >> s[i].name;
scanf("%d%d%d", &s[i].grade, &s[i].gender, &s[i].no);
}
sort(s, s + n);
scanf("%d", &m);
for (int i = 0; i < m; i++) {
scanf("%d", &op);
if (op == 1) {
string temp;
cin >> temp;
stu *p = lower_bound(s, s + n, stu(0, 0, 0, temp));
if (p->name == temp)
printf("%d %d %d\n", p->grade, p->no, p->gender);
} else if (op == 2) {
int score;
scanf("%d", &score);
for (int j = 0; j < n; j++)
if (s[j].grade == score) printf("%s\n", s[j].name.c_str());
}
}
// fclose(stdin);
// fclose(stdout);
return 0;
}
G 贪吃的派蒙
题意
有
k
k
k 份点心,
n
n
n 个人排成一个队列轮流吃,队首的人吃完则会进入回到队尾,等待下一轮。
每个人有每次都有一个吃点心的上限
a
i
a_i
ai,即轮到第
i
i
i 个人吃的时候,每轮最多吃
a
i
a_i
ai个,最少吃
1
1
1 个。
在这个队列中有一个特殊的人,她的上限是所有人中最高的,而且她每次都会尽可能吃最多的点心,队列中的其他人想要让某次轮到她的时候刚好吃完
k
k
k 份点心。问是否可行,可行输出YES
,否则输出NO
。
思路
寻找到数组 a a a 中的最大值,记录在最大值左边的最大、最小和与右边的最大、最小和,计算从第一次轮到这个人开始,寻找是否存在以下情况:
- 所有人尽可能少进食点心,总共消耗的点心数小于等于 k k k。
- 所有人尽可能多进食点心,总共消耗的点心数大于等于 k k k。
当存在以上情况,则说明存在刚好轮到此人进食时, k k k 份点心刚好全部消灭
Accepted code
/*
* @Autor: CofDoria
* @Date: 2021-01-30 12:51:38
* @LastEditTime: 2021-02-08 15:56:46
*/
#include <bits/stdc++.h>
using namespace std;
#define db double
#define ll long long
#define inf 0x3f3f3f3f
#define s(a, n) memset(a, n, sizeof(a))
#define debug(a) cout << '#' << a << '#' << endl
#define rep(l, a, b) for (ll l = a; l < b; ++l)
#define per(l, a, b) for (ll l = a; l >= b; --l)
#define _ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define _forit(i, c) \
for (__typeof__((c).begin()) i = (c).begin(); i != (c).end(); ++i)
bool fi = true;
const unsigned long long MOD = 1e9 + 7;
inline ll gcd(ll a, ll b) { return (b == 0 ? a : gcd(b, a % b)); }
ll t;
ll n, k;
ll a[100005];
ll maxn;
ll a1, a2;
ll a1num;
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
scanf("%lld", &t);
while (t--) {
maxn = 0;
scanf("%lld%lld", &n, &k);
rep(i, 0, n) {
scanf("%lld", &a[i]);
if (a[i] > maxn) {
maxn = a[i];
a1num = i;
}
}
bool flag = false;
a1 = a2 = 0;
rep(i, 0, n) {
if (!flag) {
if (a[i] == maxn) {
flag = true;
} else
a1 += a[i];
} else
a2 += a[i];
}
ll round = 0;
flag = false;
while (1) {
if (a1num + round * (maxn + n - 1) > k) break;
if (a1num + round * (maxn + n - 1) <= k &&
a1 + round * (a1 + maxn + a2) >= k) {
flag = true;
printf("YES\n");
break;
}
++round;
}
if (!flag) printf("NO\n");
}
// fclose(stdin);
// fclose(stdout);
return 0;
}
H 数羊
题意
给定两个数
n
n
n、
m
m
m,并按以下方法计算并输出结果
思路
打表找规律,可以发现当 m = 0 m=0 m=0 时结果等于 n + 2 n+2 n+2,( n n n 为 1 1 1 时除外); m = 1 m=1 m=1 时,结果为 2 ∗ n 2*n 2∗n; m = 2 m=2 m=2 时,结果为 2 n 2^n 2n。
Accepted code
/*
* @Autor: CofDoria
* @Date: 2021-01-30 16:01:23
* @LastEditTime: 2021-01-31 20:28:19
*/
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
int t;
long long n, m;
long long MOD = 998244353;
// long long A(long long n, long long m) {
// if (n == 1 && m == 0) return 2;
// if (n == 0) return 1;
// if (m == 0) return (n + 2) % MOD;
// return A(A(n - 1, m), m - 1) % MOD;
// }
long long f(long long n) {
long long ans = 1;
long long t = n;
while (t) {
ans *= 2;
ans -= (ans > MOD ? MOD : 0);
--t;
}
return ans;
}
int main() {
// freopen("out.txt", "w", stdout);
// for (long long i = 1; i <= 1000; i++) {
// for (long long j = 0; j <= 2; j++) {
// printf("#A(%lld,%lld):%lld# ", i, j, A(i, j));
// if (j == 2) puts("");
// }
// }
while (~scanf("%d", &t)) {
while (t--) {
scanf("%lld%lld", &n, &m);
if (m == 0) {
if (n == 1)
puts("2");
else
printf("%lld\n", (n + 2) % MOD);
} else if (m == 1) {
printf("%lld\n", (2 * n) % MOD);
} else {
printf("%lld\n", f(n));
}
}
}
// fclose(stdout);
return 0;
}
I 买花
题意
需要买
n
n
n 朵花,花要分
k
k
k 天买(
k
>
1
k>1
k>1),第一天可以买任意朵,之后每一天都要买前一天的
2
2
2 倍,最多不超过
15
15
15 天,若刚好买到
n
n
n 朵花,输出YE5
,否则输出N0
。
思路
通过打表发现,只要
n
n
n 为
3
,
7
,
15
,
31
…
…
3, 7, 15, 31……
3,7,15,31……的倍数(该数列是第i-1天的总购花数数列,以第一天买一朵花为基础),就可以刚好买到
n
n
n 朵花。注意输出不是YES
和NO
。
Accepted code
#include <cstdio>
using namespace std;
int t;
long long n;
int a[] = {1, 3, 7, 15, 31, 63, 127, 255,
511, 1023, 2047, 4095, 8191, 16383, 32767};
int main() {
// printf("int a[]={");
// bool fi=true;
// for(int i=0,l=1;i<15;i++){
// if(fi)fi=!fi;
// else printf(",");
// printf("%d",l);
// l*=2;
// ++l;
// }
// printf("};");
scanf("%d", &t);
while (t--) {
scanf("%lld", &n);
bool ok = false;
for (int i = 1; i < 15; i++) {
if (n % a[i] == 0) {
ok = true;
break;
}
}
if (ok)
printf("YE5\n");
else
printf("N0\n");
}
}
J 这是一题简单的模拟
题意
给出一张图和
k
k
k 条路线,问
k
k
k 条路线中对所有点遍历有且仅有一次的路线是否存在(其中0
默认为起点和终点),并输出路径长度最小的总权值。
思路
按题意模拟即可
与2020年天梯赛 L2-4 网红点打卡攻略
题完全一致,仅范围和输出内容小有更改。
Accepted code
/*
* @Autor: CofDoria
* @Date: 2020-11-28 13:31:52
* @LastEditTime: 2021-01-30 23:15:33
*/
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
int a[305][305];
int vis[305];
int n, m;
int k;
int ans[305];
// bool ok;
int minc = (int)(1e9 + 7);
int cost;
int cnt = 0;
int ansn;
int i;
void dfs(int idx) {
if (idx + 1 == n) {
if (a[ans[idx]][0]) {
cnt++;
cost += a[ans[idx]][0];
if (minc > cost) {
ansn = i;
minc = cost;
}
return;
}
return;
}
if (vis[ans[idx + 1]] || !a[ans[idx]][ans[idx + 1]]) {
// ok = false;
return;
}
cost += a[ans[idx]][ans[idx + 1]];
vis[ans[idx]] = 1;
dfs(idx + 1);
}
int main() {
scanf("%d%d", &n, &m);
memset(a, 0, sizeof(a));
for (int iii = 0; iii < m; iii++) {
int s, t, v;
scanf("%d%d%d", &s, &t, &v);
if (!a[s][t] || (a[s][t] > v)) {
a[s][t] = v;
a[t][s] = v;
}
}
scanf("%d", &k);
for (i = 1; i <= k; i++) {
int nn;
scanf("%d", &nn);
for (int ii = 0; ii < nn; ii++) {
scanf("%d", &ans[ii]);
}
if (nn == n && a[0][ans[0]]) {
memset(vis, 0, sizeof(vis));
// ok = true;
cost = a[0][ans[0]];
dfs(0);
}
}
// printf("%d\n%d %d", cnt, ansn, minc);
printf("%d", minc == (int)(1e9 + 7) ? -1 : minc);
return 0;
}
K 黑洞密码
题意
给出一段字符串
1.确定讯息的长度为 32 32 32;
2.字符串中第 4 n + 1 ∼ 4 n + 4 4n+1\sim4n+4 4n+1∼4n+4的字母和第 4 n + 4 4n+4 4n+4的字母和第 4 n + 1 ∼ 4 n + 4 4n+1\sim4n+4 4n+1∼4n+4 ( 0 ≤ n ≤ 3 ) (0 \leq n \leq 3) (0≤n≤3)的数字为一组,共 4 4 4 组;
3.每组的第1,2,3,4个字符分别往后推每组第1,2,3,4个数字个数 例:如第一个字母为a,第一个数字为3,转换后变为d,‘z’之后是’B’,‘Z’之后是’b’;
4.将每组内部字母的顺序颠倒;
5.将四组字符合并就是最后的讯息。
思路
按题意模拟即可
Accepted code
/*
* @Autor: CofDoria
* @Date: 2021-01-31 11:09:14
* @LastEditTime: 2021-02-01 12:01:18
*/
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
int zcnt = 0, scnt = 0;
char a[16];
int b[16];
char str[45];
string s[4];
int main() {
scanf("%s", &str);
for (int i = 0, sz = strlen(str); i < sz; i++) {
if (isalpha(str[i])) {
if (zcnt < 16) {
a[zcnt] = str[i];
++zcnt;
}
}
if (isdigit(str[i])) {
if (scnt < 16) {
b[scnt] = str[i] - '0';
++scnt;
}
}
if (scnt >= 16 && zcnt >= 16) break;
}
for (int i = 0; i < 4; i++) s[i].resize(4);
for (int i = 0; i < 16; i++) {
while (b[i]) {
if (a[i] == 'Z') {
a[i] = 'b';
} else if (a[i] == 'z') {
a[i] = 'B';
} else
++a[i];
--b[i];
}
s[i / 4][i % 4] = a[i];
}
for (int i = 0; i < 4; i++)
reverse(s[i].begin(), s[i].end()), printf("%s", s[i].c_str());
}
L 建立火车站
题意
有 n n n 个坐标,在其中加入 k k k 个坐标,使两两坐标之间的差尽可能小。
思路
二分寻找符合条件的最小差。
Accepted code
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
int t;
long long n, k;
long long a[100005];
long long _ceil(long long a, long long b) {
long long temp = (long long)(a / b);
if (temp * b == a) return temp;
return temp + 1;
}
bool f(long long dist) {
long long lv = k;
for (int i = 1; i < n; i++) {
if (a[i] - a[i - 1] > dist) {
long long num = _ceil((a[i] - a[i - 1]), dist) - 1;
// printf("%lld %lld\n",_ceil((a[i]-a[i-1]),dist),dist);
if (lv - num >= 0) {
lv -= num;
} else
return false;
}
}
return true;
}
int main() {
long long l = 0, r = 1, mid;
scanf("%lld%lld", &n, &k);
for (int i = 0; i < n; i++) {
scanf("%lld", &a[i]);
}
sort(a, a + n);
for (int i = 1; i < n; i++) r = max(r, a[i] - a[i - 1]);
mid = (l + r) >> 1;
while (r - l > 1) {
// printf("%lld %lld %lld\n",l,mid,r);
mid = (l + r) >> 1;
if (f(mid))
r = mid;
else
l = mid;
}
printf("%lld", r);
}