本文发布于博客园,会跟随补题进度实时更新,若您在其他平台阅读到此文,请前往博客园获取更好的阅读体验。
跳转链接:https://www.cnblogs.com/TianTianChaoFangDe/p/18848680
开题 + 补题情况
这场难度不算很大,但有点吃思维,感觉想出来了的话代码量都不大。
1007 - 架子鼓
不难发现,只需要对所有数字乘上 \(q\) 的数据范围的最小公倍数,就可以把所有分数转化为整数了,然后就可以用 \(map\) 来记录其中一个,查询另外一个来得到同时击打的时刻数量了。
点击查看代码
cpp
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1
using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;
const int N = 2e5 + 9;
i64 lc;
i64 lcm(i64 x, i64 y) {
return x / std::gcd(x, y) * y;
}
void solve()
{
int n, m;std::cin >> n >> m;
std::vector<i64> a(n), b(m);
for(int i = 0;i < n;i ++) {
i64 p, q;std::cin >> p >> q;
p = p * lc / q;
a[i] = p;
}
for(int i = 0;i < m;i ++) {
i64 p, q;std::cin >> p >> q;
p = p * lc / q;
b[i] = p;
}
std::map<i64, bool> vis;
i64 sum = 0;
for(int i = 0;i < n;i ++) {
vis[sum] = true;
sum += a[i];
}
i64 ans = 0;
sum = 0;
for(int i = 0;i < m;i ++) {
if(vis.count(sum))ans ++;
sum += b[i];
}
std::cout << ans << '\n';
}
/*1,2,3,4,6,8,16*/
int main()
{
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
lc = lcm(1, 2);
lc = lcm(lc, 3);
lc = lcm(lc, 4);
lc = lcm(lc, 6);
lc = lcm(lc, 8);
lc = lcm(lc, 16);
int t = 1;std::cin >> t;
while(t --)solve();
return 0;
}
1003 - 独到寒山顶
一开始没注意到可以往下走,以为是一个 DP,结果一看可以往下走,并且数据范围乘起来也就 \(250000\),那不就直接 \(BFS\) 最短路嘛。
点击查看代码
cpp
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1
using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;
const int N = 2e5 + 9;
struct Node {
int x, y, d;
};
void solve()
{
int n, m;std::cin >> n >> m;
std::vector a(n + 2, std::vector<int>(m + 2));
std::vector vis(n + 2, std::vector<int>(m + 2));
for(int i = 1;i <= n;i ++) {
int k;std::cin >> k;
for(int j = 1;j <= k;j ++) {
int x;std::cin >> x;
a[i][x] = true;
}
}
std::queue<Node> q;
for(int i = 1;i <= n;i ++) {
if(a[i][1] == 1)continue;
q.push({i, 1, 1});
}
while(q.size()) {
auto [x, y, d] = q.front();
q.pop();
if(vis[x][y])continue;
vis[x][y] = true;
if(y == m) {
std::cout << d << '\n';
return;
}
if(x - 1 >= 1 && a[x - 1][y] != 1) {
q.push({x - 1, y, d + 1});
}
if(x + 1 <= n && a[x + 1][y] != 1) {
q.push({x + 1, y, d + 1});
}
if(y + 1 <= m && a[x][y + 1] != 1) {
q.push({x, y + 1, d + 1});
}
if(y - 1 >= 1 && a[x][y - 1] != 1) {
q.push({x, y - 1, d + 1});
}
}
}
int main()
{
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int t = 1;std::cin >> t;
while(t --)solve();
return 0;
}
1001 - 拼图游戏
对于这个题,我们可以考虑 \(O(n \log n)\) 的做法,我们枚举 \(x\) 的大小,由于只要有一个 \(y\) 满足了题意,则更大的 \(y\),也一定满足,因此可以二分最小满足的 \(y\) 值。
至于颜色,我们可以用一个 \(bitset\) 来记录,这样的话,只需要调用 \(bitset\) 的 \(all()\) 函数就可以判断是否所有颜色都出现过了,不过要注意,由于 \(bitset\) 的大小是在编译时就决定了的,不可变的,因此只能直接往最大了开,因此要预先把不合法的颜色设置为 \(1\)。
点击查看代码
cpp
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1
using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;
const int N = 2e5 + 9;
void solve()
{
int n, m, k;std::cin >> n >> m >> k;
std::vector a(m, std::vector<int>(n));
for(int j = 0;j < n;j ++) {
for(int i = 0;i < m;i ++) {
std::cin >> a[i][j];
}
}
std::vector<std::bitset<2001>> pre(m);
for(int i = 0;i < m;i ++) {
pre[i].set(0, 1);
for(int j = k + 1;j <= 2000;j ++) {
pre[i].set(j, 1);
}
}
i64 ans = 0;
for(int j = 0;j < n;j ++) {
for(int i = 0;i < m;i ++) {
pre[i] = pre[i].set(a[i][j], 1);
if(i - 1 >= 0)pre[i] = pre[i] | pre[i - 1];
}
int l = -1, r = m;
while(l + 1 < r) {
int mid = l + r >> 1;
if(pre[mid].all())r = mid;
else l = mid;
}
ans += m - r;
}
std::cout << ans << "\n";
}
int main()
{
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int t = 1;std::cin >> t;
while(t --)solve();
return 0;
}
1002 - 缓存系统
01 背包板题,由于数据出锅导致众多人最后才做出来。
这里和 01 背包板子题唯一不同就是,对于每一行,有很多个不同的前缀取值,但是只能取一个,因此,转移的时候,不能把行这个维度省略掉,要从上一行转移过来。
除此之外,还不要忘了不选这一行的任何值直接继承上一行的情况(本人因为这个被卡了 \(20\) 分钟)。
点击查看代码
cpp
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1
#define int long long
using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;
const int N = 2e5 + 9;
void solve()
{
int n, m, x;std::cin >> n >> m >> x;
std::vector a(n + 1, std::vector<int>(m + 1)); // 空间
std::vector b(n + 1, std::vector<int>(m + 1)); // 次数
std::vector dp(n + 1, std::vector<int>(x + 1));
int sum = 0;
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= m;j ++) {
std::cin >> a[i][j] >> b[i][j];
sum += b[i][j];
a[i][j] += a[i][j - 1];
b[i][j] += b[i][j - 1];
}
}
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= m;j ++) {
for(int k = 0;k <= x;k ++) {
dp[i][k] = std::max(dp[i][k], dp[i - 1][k]);
}
for(int k = x;k - a[i][j] >= 0;k --) {
dp[i][k] = std::max(dp[i - 1][k - a[i][j]] + b[i][j], dp[i][k]);
}
}
}
int ans = 0;
for(int i = 1;i <= n;i ++) {
for(int j = 0;j <= x;j ++) {
ans = std::max(ans, dp[i][j]);
}
}
std::cout << sum - ans << '\n';
}
signed main()
{
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int t = 1;std::cin >> t;
while(t --)solve();
return 0;
}
1010 - 字符串哈希(补题)
这个题,还真要用上字符串哈希的一些思考方向才能 get 到出题人想让你干什么。
分别分析一下 \(A(s)\) 和 \(B(s)\) 有什么性质。
- 由于 \(A(s)\) 是没有取模的,因此每一个 \(A(s)\) 的值,对应唯一的一个字符串。
- 由于 \(B(s)\) 对 \(10007\) 取模了,所以 \(B(s)\) 的取值范围被限制在了 \([0, 10007)\)。
那么,我们可以枚举 \(B(s)\) 的值呀!我们枚举 \(B(s)\) 的值,并且按照计算公式计算出 \(A(s)\) 的值,再用 \(A(s)\) 的值还原出字符串的 \(ord\) 序,判断是否合法(是否超出长度范围或者是否出现了 \(0\) 作为 \(ord\)),如果合法,再按 \(B(s)\) 的规则还原出 \(B(s)\) 的值,判断是否和枚举的 \(B(s)\) 的值相同,如果相同,则答案 \(+1\)。
点击查看代码
cpp
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1
#define int long long
using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;
const int N = 2e5 + 9;
const int M = 10007;
void solve()
{
int k, c, d, e, f;std::cin >> k >> c >> d >> e >> f;
std::vector<int> ci(16);
ci[0] = 1;
for(int i = 1;i <= 15;i ++) {
ci[i] = (ci[i - 1] * 10) % M;
}
int ans = 0;
std::vector<int> tmp;
for(int i = 0;i < M;i ++) {
tmp.clear();
__int128_t now = c * i * i * i + d * i * i + e * i + f;
while(now) {
tmp.push_back((int)(now % 27));
now /= 27;
}
if(tmp.size() > k)continue;
if(tmp.size() == 0)continue;
bool ck = false;
int p = 0;
for(int i = 0;i < tmp.size();i ++) {
if(tmp[i] == 0)ck = true;
p = (p + tmp[i] * ci[i]) % M;
}
if(ck)continue;
if(p == i) {
ans ++;
}
}
std::cout << ans << '\n';
}
signed main()
{
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int t = 1;std::cin >> t;
while(t --)solve();
return 0;
}
1005 - 咒语附魔(补题)
听说是 CF 的一个原题()
这个题,一开始拿 \(bitset\) 瞎搞了一发,意料之内地 TLE 了。
这个题,有一个你以为只是出题人偷懒但实际上就是做题的关键的信息:B 中每个位置上的数字等概率地生成为 \(0\) 或 \(1\)。
这意味着什么?
首先想一下,要想让结果尽可能地大,那是不是就是前缀连续 \(1\) 的长度尽可能地长?
那么,我们要找的,就是连续的前缀 \(1\) 的长度,又因为 B 中每个位置上的数字等概率地生成为 \(0\) 或 \(1\),所以,每次都有 \(1/2\) 的概率,在该位置异或得到 \(0\),也就会跳出循环,那么,一定会很快就会跳出循环,所以循环次数一定不会多,也就是......暴力就行了。
找到最长的前缀 \(1\) 的长度,对这些串收集起来,排序取最大,数 \(1\) 的个数即可。
点击查看代码
cpp
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1
using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;
const int N = 2e5 + 9;
void solve()
{
int n, m;std::cin >> n >> m;
std::string s, t;std::cin >> s >> t;
std::vector<std::vector<int>> g(n + 1);
for(int i = 0;i + n - 1 < t.size();i ++) {
int sum = 0;
for(int j = i;j < t.size() && sum < n;j ++) {
if(t[j] != s[j - i])sum ++;
else break;
}
g[sum].push_back(i);
}
std::vector<std::string> ans;
for(int i = n;i >= 0;i --) {
if(g[i].size()) {
for(auto &j : g[i]) {
std::string tmp = "";
for(int k = j;k <= j + n - 1;k ++) {
tmp += ((t[k] == s[k - j]) ? '0' : '1');
}
ans.push_back(tmp);
}
break;
}
}
std::sort(ans.begin(), ans.end());
int sum = 0;
if(!ans.size()) {
std::cout << 0 << '\n';
} else {
for(auto &i : ans[ans.size() - 1]) {
sum += (i == '1');
}
std::cout << sum << '\n';
}
}
int main()
{
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int t = 1;std::cin >> t;
while(t --)solve();
return 0;
}