A.Leap Year(思维)
题意:
给你一个介于 1583 1583 1583和 2023 2023 2023之间的整数 Y Y Y。
求公历 Y Y Y年的天数。
在给定的范围内, Y Y Y年的天数如下:
- 如果 Y Y Y不是 4 4 4的倍数,则为 365 365 365天;
- 如果 Y Y Y是 4 4 4的倍数,但不是 100 100 100的倍数,则为 366 366 366天;
- 如果 Y Y Y是 100 100 100的倍数,但不是 400 400 400的倍数,则为 365 365 365天;
- 如果 Y Y Y是 400 400 400的倍数,则为 366 366 366天。
分析:
判断年份是否为闰年。
- 如果 n n n不能被 4 4 4整除,那么不是闰年。
- 如果 n n n可以被 400 400 400整除,那么是闰年。
- 如果 n n n不可以被 100 100 100整除但是可以被 4 4 4整除,那么是闰年。
否则就不是闰年。使用if-else
语句判断即可。
代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
if (n % 4 != 0) {
cout << 365 << endl;
} else if (n % 400 == 0) {
cout << 366 << endl;
} else if ((n % 100 != 0) && n % 4 == 0) {
cout << 366 << endl;
} else {
cout << 365 << endl;
}
return 0;
}
B.Second Best(模拟)
题意:
给你一个长度为 N N N的整数序列 A = ( A 1 , … , A N ) A=(A_1,\ldots,A_N) A=(A1,…,AN)。这里的 A 1 , A 2 , … , A N A_1,A_2,\ldots,A_N A1,A2,…,AN都是不同的。
在 A A A中,哪个元素是第二大元素?
分析:
按照题意,使用map
、pair
或结构体对元素进行排序即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1000100;
struct s {
int x, id;
bool operator<(const s &r) const {
return x > r.x;
}
} a[N];
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i].x;
a[i].id = i;
}
sort(a + 1, a + n + 1);
cout << a[2].id << endl;
return 0;
}
C.Transportation Expenses(二分答案)
题意:
有 N N N人参加一项活动,第 i i i个人的交通费用是 A i A_i Ai日元。
活动组织者高桥决定设定交通补贴的最高限额为 x x x。第 i i i个人的补贴为 min ( x , A i ) \min(x,A_i) min(x,Ai)日元。这里, x x x必须是一个非负整数。
高桥的预算为 M M M日元,他希望所有 N N N人的交通补贴总额最多为 M M M日元,那么补贴限额 x x x的最大可能值是多少?
如果补贴限额可以无限大,输出infinite
。
分析:
本题我们分情况讨论,首先考虑答案为无限大的情况。对于这种情况可以先计算序列的总和和 M M M比较,如果这个都小于等于 M M M,那么肯定答案是无穷大。
对于不是无穷大的情况,考虑尝试枚举一个 x x x,然后对于每一个 x x x判断是否满足题目条件,然后记录最大的 x x x,时间复杂度 O ( N M ) O(NM) O(NM)。
对 x x x的枚举我们采用二分,然后来判断,题目要求 x x x的最大值,可以发现若此时的 x x x为最大,那么对于每一个 k ≤ x k\le x k≤x都可以满足题条件,而对于每一个 k ≥ x k\ge x k≥x都不能满足题目条件,满足单调性。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 200005;
LL n, m, a[N], ans = -1e17;
bool check(LL x) {
LL res = 0;
for (int i = 1; i <= n; i++)
res += min(a[i], x);
return (res <= m);
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
if (check(1e17)) {
cout << "infinite" << endl;
return 0;
}
LL l = 0, r = m;
while (l <= r) {
LL mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
l = mid + 1;
} else
r = mid - 1;
}
cout << ans << endl;
return 0;
}
D.AtCoder Janken 3(动态规划)
题意:
高桥和青木玩了 N N N次剪刀石头布。注:在这个游戏中,石头赢剪刀,剪刀赢布,布赢石头。
青木的动作由长度为 N N N的字符串 S S S表示,字符串由"R"、"P"和"S"组成。 S S S中的第 i i i个字符表示青木在第 i i i个对局中的出招:“R"表示"石头”,“P"表示"布”,“S"表示"剪刀”。
高桥的出招满足以下条件:
- 高桥从未输给过青木。
- 对于 i = 1 , 2 , … , N − 1 i=1,2,\ldots,N-1 i=1,2,…,N−1,高桥在第 i i i场对局中的出招与他在第 ( i + 1 ) (i+1) (i+1)场对局中的出招不同。
确定高桥可能赢得的最大对局数。
可以保证存在一个满足上述条件的高桥出招顺序。
分析:
因为相邻两场对局出招不同,发现之前的对局存在后效性,考虑动态规划。
设 d p i , [ 0 / 1 / 2 ] dp_{i,[0/1/2]} dpi,[0/1/2]表示当前为第i场对局,当前出的拳是 R , P , S R,P,S R,P,S,最多可以赢多少局。
特殊计算一下 d p 1 , [ 0 / 1 / 2 ] dp_{1,[0/1/2]} dp1,[0/1/2], d p i , j dp_{i,j} dpi,j的值可以通过 d p i − 1 , k dp_{i-1,k} dpi−1,k转移来当且仅当 k ≠ j k\neq j k=j,对于会输的出招直接赋值为一个极小值,取一个最大值然后增加对答案的贡献即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1000100;
const int MIN_INF = -2e9;
int n;
int dp[N][3];
int main() {
cin >> n;
string s;
cin >> s;
s = ' ' + s;
if (s[1] == 'R') {
dp[1][0] = 0;
dp[1][1] = 1;
dp[1][2] = MIN_INF;
} else if (s[1] == 'P') {
dp[1][0] = MIN_INF;
dp[1][1] = 0;
dp[1][2] = 1;
} else {
dp[1][0] = 1;
dp[1][1] = MIN_INF;
dp[1][2] = 0;
}
for (int i = 2; i <= n; ++i) {
if (s[i] == 'R') {
dp[i][0] = max({dp[i - 1][1], dp[i - 1][2]});
dp[i][1] = max({dp[i - 1][0], dp[i - 1][2]}) + 1;
dp[i][2] = MIN_INF;
} else if (s[i] == 'P') {
dp[i][0] = MIN_INF;
dp[i][1] = max({dp[i - 1][0], dp[i - 1][2]});
dp[i][2] = max({dp[i - 1][0], dp[i - 1][1]}) + 1;
} else {
dp[i][0] = max({dp[i - 1][1], dp[i - 1][2]}) + 1;
dp[i][1] = MIN_INF;
dp[i][2] = max({dp[i - 1][0], dp[i - 1][1]});
}
}
cout << max({dp[n][0], dp[n][1], dp[n][2]}) << endl;
return 0;
}
E.Xor Sigma Problem(数学)
题意:
给你一个长度为 N N N的整数序列 A = ( A 1 , … , A N ) A=(A_1,\ldots,A_N) A=(A1,…,AN)。求以下表达式的值:
∑ i = 1 N − 1 ∑ j = i + 1 N ( A i ⊕ A i + 1 ⊕ … ⊕ A j ) \displaystyle\sum_{i=1}^{N-1}\sum_{j=i+1}^N(A_i\oplus A_{i+1}\oplus\ldots\oplus A_j) i=1∑N−1j=i+1∑N(Ai⊕Ai+1⊕…⊕Aj)
分析:
看到异或我们考虑拆位计算答案。设当前枚举到第
k
k
k位,这一位有
i
i
i个0
,
j
j
j个1
,因为异或为
1
1
1要求两个异或的数互不相同,所以这一位对答案的贡献即为
i
×
j
×
2
k
i\times j\times 2^k
i×j×2k。
将所有位对答案的贡献累加即可。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 200001;
LL a[N], s[N];
int main() {
LL n;
cin >> n;
for (LL i = 1; i <= n; ++i) {
cin >> a[i];
s[i] = a[i];
a[i] ^= a[i - 1];
}
LL res = 0;
for (LL k = 0; k < 30; ++k) {
LL cnt = 0;
for (LL j = 0; j <= n; ++j)
cnt += a[j] >> k & 1;
res += cnt * (n - cnt + 1) * (1LL << k);
}
for (LL i = 1; i <= n; ++i)
res -= s[i];
cout << res << endl;
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。