比赛链接
本文发布于博客园,会跟随补题进度实时更新,若您在其他平台阅读到此文,请前往博客园获取更好的阅读体验。
跳转链接:https://www.cnblogs.com/TianTianChaoFangDe/p/18773190
开题 + 补题情况
很唐的一场比赛,前四个签到题都做了八百年,然后又被博弈论硬控了,
1002 - 学历史导致的
签到题,由于数据范围只有几十,所以直接枚举暴搜就行。
点击查看代码
#include <bits/stdc++.h>
#define inf 2e18
#define int long longconst 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 就行了。
点击查看代码
#include <bits/stdc++.h>
#define inf 2e18
#define int long longconst 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\) 也被算进去了。
点击查看代码
#include <bits/stdc++.h>
#define inf 2e18
#define int long longconst 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\) 将块中的元素个数的贡献加上来即可。
点击查看代码
#include <bits/stdc++.h>
#define inf 2e18
#define int long longconst 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 游戏了。
好像是很常用的套路,算是学到了。
点击查看代码
#include <bits/stdc++.h>
#define inf 2e18
#define int long longconst 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";}
}