本文发布于博客园,会跟随补题进度实时更新,若您在其他平台阅读到此文,请前往博客园获取更好的阅读体验。
跳转链接:https://www.cnblogs.com/TianTianChaoFangDe/p/18773190
开题 + 补题情况
很唐的一场比赛,前四个签到题都做了八百年,然后又被博弈论硬控了,
1002 - 学历史导致的
签到题,由于数据范围只有几十,所以直接枚举暴搜就行。
点击查看代码
cpp
#include <bits/stdc++.h>
#define inf 2e18
#define int long long
const int N = 2e5 + 9;
std::string a[] = {"jia", "yi", "bing", "ding", "wu", "ji", "geng", "xin", "ren", "gui"};
std::string b[] = {"zi", "chou", "yin", "mao", "chen", "si", "wu", "wei", "shen", "you", "xu", "hai"};
void solve()
{
std::string s;std::cin >> s;
for(int i = 1984;i <= 2043;i ++) {
int ch = i - 1984;
int x = ch % 10;
int y = ch % 12;
std::string t = a[x] + b[y];
if(s == t) {
std::cout << i << '\n';
return;
}
}
}
1004 - 学 DP 导致的
注意到最长答案只会是 \(26\),所以最多复制26次就行。
乍一看以为要用单调栈优化法或线段树优化法找最长上升子序列,结果定睛一看,都是小写字母,那范围也就 \(26\),直接暴力 DP 就行了。
点击查看代码
cpp
#include <bits/stdc++.h>
#define inf 2e18
#define int long long
const int N = 2e5 + 9;
int toint(std::string &s) {
int res = 0;
for(auto &i : s) {
res = res * 10 + i - '0';
}
return res;
}
void solve()
{
std::string s;std::cin >> s;
std::string k;std::cin >> k;
int x;
if(k.size() > 2) {
x = 100;
} else {
x = toint(k);
}
std::string tmp = s;
for(int i = 1;i < x;i ++) {
s += tmp;
}
std::vector<int> dp(27, 0);
auto getint = [](const char c) -> int {
return c - 'a' + 1;
};
int ans = 0;
for(auto &i : s) {
int ix = getint(i);
for(int j = 0;j < ix;j ++) {
dp[ix] = std::max(dp[ix], dp[j] + 1);
}
ans = std::max(ans, dp[ix]);
}
std::cout << ans << '\n';
}
1003 - 学数数导致的
一开始读错题了,写了半天,以为是找不同的下标构成的题目的构造,然后定睛一看是找不同的数字构成的,这难度一下就下降了不少。
我们可以动态维护一下当前前缀每个数字的总数,然后从后往前遍历,记录一下后面出现了多少个不同的正整数 \(sum\),然后对于每一个数字,如果有间隔 \(0\) 的它自己,就更新一下它的答案最大值,也就是当前的 \(sum\),最后求一个和。
这个题有一个很关键的点在于要以 \(0\) 为分界点清除每个数字作为 \(p\) 的数量,否则可能不间隔 \(0\) 也被算进去了。
点击查看代码
cpp
#include <bits/stdc++.h>
#define inf 2e18
#define int long long
const int N = 1e6;
void solve()
{
int n;std::cin >> n;
std::vector<int> a(n);
for(auto &i : a) {
std::cin >> i;
}
std::vector<int> cnt(N + 9, 0), all(N + 9, 0);
std::vector<bool> vis(N + 9, 0);
for(int i = 0;i < n;i ++) {
cnt[a[i]] ++;
}
int ans = 0;
int sum = 0;
vis[0] = true;
for(int i = n - 1;i >= 0;i --) {
if(a[i] == 0) {
cnt[a[i]] --;
continue;
}
if(!cnt[0])break;
int ix;
for(int j = i;j >= 0 && a[j] != 0;j --) {
cnt[a[j]] --;
ix = j;
}
for(int j = i;j >= 0 && a[j] != 0;j --) {
if(a[j] && cnt[a[j]]) {
all[a[j]] = std::max(all[a[j]], sum);
}
sum += !vis[a[j]];
vis[a[j]] = true;
}
i = ix;
}
for(int i = 1;i <= N;i ++) {
ans += all[i];
}
std::cout << ans << "\n";
}
1005 - 学几何导致的
不难发现,垂直的,其实就是旋转了 \(90\) 度的,也就是说:\((180 / k) \times x = 90 \times p\),\(p\) 为奇数,移项消元后可以得到:\(2 \times x = k \times p\),式子左边一定是一个偶数,\(p\) 是一个奇数,若要让式子成立,\(k\) 必须是一个偶数,因此所有奇数 \(k\) 答案都是 \(0\)。
然后,我们求出的 \(x\),也就是一个 \(90\) 度的循环,那么我们按 \(x\) 进行分块,对于奇数块中的线,和它垂直的就是偶数块中的对应位置的线,对于偶数块中的线,和它垂直的就是奇数块中的对应位置的线。
我们首先将最后对 \(2 \times x\) 取模后不完整的块往前匹配特殊计算一下。
然后对于剩下的,为了避免计算重复,每个块只和后面的块匹配,不难发现对于奇数块可以得到一个 \(1\) ~ \(n / 2x\) 的公差为 \(1\) 的等差数列,对于偶数块可以得到一个 \(1\) ~ \(n / 2x - 1\) 的公差为 \(1\) 的等差数列,分别对等差数列求和后,再乘一个 \(x\) 将块中的元素个数的贡献加上来即可。
点击查看代码
cpp
#include <bits/stdc++.h>
#define inf 2e18
#define int long long
const int N = 2e5 + 9;
void solve()
{
int n, k;std::cin >> n >> k;
int ans = 0;
if(k & 1) {
std::cout << 0 << '\n';
return;
}
int x = k / 2;
int ch = n % x;
n -= ch;
int p = n / x;
if(p & 1) {
int sum = n / (2 * x) + 1;
ans += ch * sum;
} else {
int sum = n / (2 * x);
ans += ch * sum;
}
ch = n % (2 * x);
n -= ch;
if(ch) {
int sum = n / (2 * x);
ans += ch * sum;
}
int sum = n / (2 * x);
if((sum + 1) & 1) {
ans += sum / 2 * (sum + 1) * x;
ans += sum / 2 * (sum - 1) * x;
} else {
ans += (sum + 1) / 2 * sum * x;
ans += (sum - 1) / 2 * sum * x;
}
std::cout << ans << '\n';
}
1006 - 学博弈论导致的(补题)
被博弈论创似了。
题目说了一大堆,但如果给红宝石赋值为 \(1\),蓝宝石赋值为 \(2\),宝箱赋值为 \(4\),就可以把题目中所有操作拿掉的值控制在 \(1\) ~ \(3\),然后就是一个 NIM 游戏了。
好像是很常用的套路,算是学到了。
点击查看代码
cpp
#include <bits/stdc++.h>
#define inf 2e18
#define int long long
const int N = 2e5 + 9;
void solve()
{
int x, y, z;std::cin >> x >> y >> z;
if((x + 2 * y) % 4 == 0) {
std::cout << "Bob\n";
} else {
std::cout << "Alice\n";
}
}