专栏文章
白哥很黑模拟赛总结
个人记录参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minpgih7
- 此快照首次捕获于
- 2025/12/02 06:12 3 个月前
- 此快照最后确认于
- 2025/12/02 06:12 3 个月前
我又没开long long!
预期:100 + 100 + 24 + 40
实际:100 + 100 + 4 + 32
T1
很快就想到了,但是写了1.5h。
题意:有一个长度为 的排列 。可以选择一个 ,并将 替换为 。求能否利用上述操作将整个排列升序排序。
正常人的想法是逆序对,但我不是正常人,所以我想到了链表。
可以发现,只要我从 到 一个个往后移,剩下 和 是移不了的。
那么我就判断一下 在 前还是后。
暴力移动是 的,总复杂度是 ,所以我们来看看如何 移动。
我们发现将 移动到 ,只需要先把 移动到 ,就可以直接移动了,剩下的顺序不变。
于是可以用链表维护,注意当 时,需要先特殊移动一次,把 移动到第二个,就可以正常操作。
CPP#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 1e6 + 5;
int t, n, a[kMaxN], pre[kMaxN], nxt[kMaxN], ed;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
for (cin >> t; t; t--) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
pre[a[i]] = a[i - 1];
nxt[a[i - 1]] = a[i];
}
nxt[a[n]] = 0;
ed = a[n];
for (int i = n; i >= 3; i--) {
if (!pre[i]) {
int x = nxt[nxt[i]];
pre[i] = x;
nxt[nxt[i]] = nxt[nxt[nxt[i]]];
pre[nxt[x]] = nxt[i];
nxt[x] = i;
pre[x] = 0;
}
if (i == ed) {
ed = pre[ed];
continue;
}
nxt[ed] = pre[i];
nxt[pre[pre[i]]] = nxt[i];
pre[nxt[i]] = pre[pre[i]];
nxt[i] = 0;
pre[pre[i]] = ed;
ed = pre[i];
}
cout << (pre[2] ? "Yes\n" : "No\n");
}
return 0;
}
T2
数学爽题,很好玩,写了1.5h。
题意:一个数,初始为 ,可以花费 把数加 ,也可以花费 把数乘 ,求把数变为 的最小花费,无解输出
-1。先特判一下 ,不然会炸。这时乘操作无效,直接看加操作即可。
不难发现,乘的次数只有 ,于是枚举乘的次数 。
这个 的上限建议算一下 ,不然数字会超级大。
先处理一下初始的 ,显然最后这个 会变成 ,所以先把 。
而加操作只有 ,所以 必须是 的倍数,否则无解。
接下来我们再把 ,现在一次加操作,就相当于给一个 进制数的一位加 。
那么把 进制下的 的后 位加起来,再加上剩下的位转化成十进制的数,即为加操作次数。
写完出去吃个包子,剩下55min,足够了。
CPP#include <bits/stdc++.h>
#define int long long
using namespace std;
int c, t, n, x1, c1, x2, c2;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
for (cin >> c >> t; t; t--) {
cin >> n >> x1 >> c1 >> x2 >> c2;
if (x2 == 1) {
n--;
cout << (n % x1 ? -1 : (n / x1) * c1) << "\n";
continue;
}
int mn = 1e18, lim = 0;
for (__int128_t i = 1; i <= n; i *= x2, lim++);
for (int i = 0, x = n, y = 1; i < lim; i++, y *= x2, x = n) {
int sum = 0, ans = 0;
ans = i * c2;
x -= y;
if (x % x1)
continue;
x /= x1;
int ty = y;
for (int j = i; j >= 0; j--) {
sum += x / ty;
x %= ty;
ty /= x2;
}
mn = min(mn, ans + sum * c1);
}
cout << (mn == 1e18 ? -1 : mn) << "\n";
}
return 0;
}
T3
没开二度。
当时我竟然没有预料到它的答案会那么大,明明有平方。
预估24pts,实际4pts,开long long后40pts写完人类智慧100pts。
题意:给定一个长度为 的序列 ,问将序列 划分成若干个连续子段的最大值减最小值的平方的最大值。
显然最优方案的每个子段两端必然是最大值和最小值,所以 做法就很显然了。
转移方程可以用李超线段树维护。
但是我不会,所以这里有另外一种能过的方法。
经过同学的实验,判掉特殊性质(单增),每个数只往前找 个就可以过(虽然可以被卡)。
所以,我们充分发扬人类智慧:
CPP#include <bits/stdc++.h>
#define int long long
using namespace std;
const int kMaxN = 1e6 + 5;
int n, a[kMaxN], f[kMaxN];
signed main() {
cin >> n;
int flag = 1;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] < a[i - 1])
flag = 0;
}
if (flag)
cout << (a[n] - a[1]) * (a[n] - a[1]);
else {
for (int i = 1; i <= n; i++)
for (int j = max(1ll, i - 20); j <= i; j++)
f[i] = max(f[i], f[j - 1] + (a[i] - a[j]) * (a[i] - a[j]));
cout << f[n];
}
return 0;
}
T4
数据又骗人,本来20个测试点,每个有5pts,8个就有40pts,结果又有25个,只剩32pts。
题意:一棵 个点的树,加了 条边,求把每个点染 种颜色之一,且相邻两点颜色不同的染色方案数。
当 时,显然除了根节点,剩下的都有 种情况,根节点有 种情况,答案就是 。
总结
我已经连续两次没开long long了🤬
上一次我吸取了同学的教训,于是我在今天空间足够的情况下
#define int long long,但是只有T2、4开了。下次打框架就加上,我已经黑化了😈
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...