[大弯曲比赛] 2024初中组一些碎碎念
posted on 2024-06-16 14:39:31 | under | source
概况:T4 看起来不太可做,磕 T1 磕出个假做法,T2 正常切,T3 最后 10 分钟想了个感觉比较对的做法,但没时间敲了qwq。
难度可能是蓝、蓝、蓝、紫/蓝吧。考前教练说“普及组难度”,呵呵。
T1
题意:\(n\) 阶汉诺塔,给出起始状态 \(S\)、终止状态 \(T\),求 \(S\to T\) 的最小步数,对 \(1e9+7\) 取模。\(n\le 1e5\)。
考虑基础汉诺塔,记 \(f_i\) 表示场上有一个柱子挂着 \(i\) 个盘子,其它柱子为空,将所有盘子移至另一个柱子的最小步数。有递推式 \(f_i=2f_{i-1}+1\),\(f_0=0\)。
还有一个性质:\(S\to T=T\to S\)。这样子可以倒推简化问题了。
马上想出一个错误的贪心,将 \(S\to I\) 和 \(I\to T\) 的步数加起来,\(I\) 是所有盘子都在一个柱子的状态。大样例根本没法过哈哈哈。
(疑似)正解:考虑将盘子 \(n\) 从起点移至目标位置,必须将所有小盘子都移到非起始位置和目标位置的那根柱子上,这一步可以倒推处理。此时 \(1\dots n-1\) 的都在一个柱子上了,直接计算就好啦。如果 \(n\) 不用移动,就考虑 \(n-1\),以此类推。
洛谷原题 P1242。
但是翻 P1242 题解区时发现有个 hack:\(\{3\},\{\},\{1,2\}\to \{1,2\},\{\},\{3\}\),上面的贪心有点问题,还待研究。
是一道比较出乎意料的贪心好题。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 5, mod = 1e9 + 7;
int n, s[N], t[N], f[N], ans, ps, sp;
inline int calc(int pos, int p, int s[]){
int res = 0, siz = p;
for(int i = p; i; --i, --siz){
if(s[i] == pos) continue;
res = (res + f[siz - 1] + 1) % mod;
pos = 6 - s[i] - pos;
}
return res;
}
signed main(){
for(int i = 1; i < N; ++i) f[i] = (f[i - 1] * 2 + 1) % mod;
cin >> n;
for(int i = 1; i <= n; ++i) scanf("%lld", &s[i]);
for(int i = 1; i <= n; ++i) scanf("%lld", &t[i]);
for(int i = n; i; --i){
if(s[i] != t[i]){
ps = i - 1, sp = 6 - s[i] - t[i];
ans = (ans + calc(6 - s[i] - t[i], i - 1, s) + 1) % mod;
for(int j = 1; j <= ps; ++j) s[j] = 6 - s[i] - t[i];
break;
}
}
ans = (ans + calc(sp, ps, t)) % mod;
cout << ans;
return 0;
}
/*
随机数据下正确
Hack:
3
1 1 3
3 1 1
*/
T2
题意:给出序列 \(a_1\dots a_n\),每次可以选择一段连续的、\(a\) 相等的区间,将其消掉,获得该区间大小的平方的价值,删掉后原序列左右相接,求最大价值?\(n\le 5e2\)。
绝大多数考场解法都能被简单 hack 掉。但就是过了,只能说数据真 jb 水。
洛谷有原题,题解。
T3
题意:动态维护一个序列 \(a_1\dots a_n\),每次加入从末端加入一个元素,然后从 \(1\) 到 \(n-1\) 进行“冒泡”:若 \(a_i<a_{i+1}\) 就交换 \(a_i,a_{i+1}\);每次询问序列第 \(x\) 个元素。共 \(m\) 次操作,\(m\le 1e6\)。
首先转化一下题意,不要每次都调整,而是将它们放在一起,查询时统一调整。
那么其实就是 \(a_i\) 可以向左冒泡 \(n-i+1\) 次,然后从 \(1\) 到 \(n\) 这样冒泡。
我们依次考虑最终序列 \(b\) 的每一个元素,可以发现,能成为 \(b_i\) 的 \(a_j\) 下标一定构成一段连续区间 \([1,r]\)。
我们以 \(2\nmid n\) 举例,记中点 \(mid=\frac {n+1}2\)。那么 \(b_1\) 属于 \([1,mid]\),\(b_2\) 属于 \([1,mid+1]\),\(b_3\) 属于 \([1,mid+1]\),\(b_4\) 属于 \([1,mid+2]\)。以此类推,是有周期性的变化。
猜了下,\(b_i\) 就是在对应区间内取最大值,但是不能和 \(b_1\dots b_{i-1}\) 重复。
容易有结论:若 \(b_i\) 和 \(b_{i-1}\) 区间重合,则 \(b_i\) 取对应区间的第 \(i\) 大值;否则取 \(b_{i-1}\) 区间的第 \(i\) 大值、\(a_i\) 中的较大者。
可持久化值域线段树能做到 \(O(m\log m)\)。
考场思路,不清楚对不对。欢迎 hack 本蒟蒻。
赛后对拍没拍出问题。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int m, n, x, a[N];
char opt[2];
namespace Zx_Tree{
#define mid (l + r >> 1)
struct node{int ls, rs, siz;} t[N * 20];
int tot, rot[N];
inline void psup(int u) {t[u].siz = t[t[u].ls].siz + t[t[u].rs].siz;}
inline void upd(int ua, int &ub, int l, int r, int k){
t[ub = ++tot] = t[ua];
if(l == r) {++t[ub].siz; return ;}
if(k <= mid) upd(t[ua].ls, t[ub].ls, l, mid, k);
else upd(t[ua].rs, t[ub].rs, mid + 1, r, k);
psup(ub);
}
inline int fid(int u, int l, int r, int k){
if(l == r) return l;
if(t[t[u].rs].siz >= k) return fid(t[u].rs, mid + 1, r, k);
return fid(t[u].ls, l, mid, k - t[t[u].rs].siz);
}
}using namespace Zx_Tree;
signed main(){
// freopen("yl.txt", "r", stdin);
// freopen("1.txt", "w", stdout);
cin >> m;
for(int i = 1; i <= m; ++i){
scanf("%s %d", opt, &x);
if(opt[0] == 'A') a[++n] = x, upd(rot[n - 1], rot[n], 1, m, x);
else{
int md = n / 2 + 1;
if(n % 2 == 1){
if(x % 2 == 1) printf("%d\n", fid(rot[md + x / 2], 1, m, x));
else printf("%d\n", max(fid(rot[md + x / 2 - 1], 1, m, x), a[md + x / 2]));
}
else{
if(x % 2 == 0) printf("%d\n", fid(rot[md + x / 2 - 1], 1, m, x));
else printf("%d\n", max(fid(rot[md + x / 2 - 1], 1, m, x), a[md + x / 2]));
}
}
}
return 0;
}
/*
10
A 3
A 6
Q 2
A 3
A 9
Q 3
Q 2
Q 1
A 4
Q 4
*/
T4
逆天模拟,不会。

浙公网安备 33010602011771号