2025.7.9 NOI 模拟赛 题解
T1 变化(change)
题意
对于字符串 \(S\),存在三类操作:删除任意一个为 \(AB,BA,AAAA,BBBB,CDC,DDD\) 的子串,替换子串 \(A\to BBB,B\to AAA,C\to DED,D\to EE,E\to CD,DC\to E\),交换相邻字母满足其中一个为 \(A/B\) 且另一个为 \(C/D/E\),给定 \(s,t\),\(q\) 次询问每次给定 \(s\) 和 \(t\) 的一个区间,判断 \(s\) 的这一区间能否用有限次上述变换变为 \(t\) 的区间,\(|s|,|t|,q\le10^5\)
分析
考虑如何判断 \(s\) 能否转化为 \(t\)
由于 \(AB\to BBBB\to BA\),\(BA\to BBBB\to AB\),\(DC\to E\to CD\),\(CD\to DEDD\to DCDDD\to DC\),以及操作三,可得五种字母可以任意重排
由于 \(A/B\) 只能替换为 \(A/B\),\(C/D/E\) 只能替换为 \(C/D/E\),考虑先将 \(A/B\) 移到字符串前面,\(C/D/E\) 移到字符串后面(由于交换操作可逆,因此对 \(s\) 和 \(t\) 同时操作)
此时前后两段分离
先考虑 \(A/B\) 部分,令 \(x\) 为 \(A\) 的数量,\(y\) 为 \(B\) 的数量,发现 \((x,y)\to (x-1,y+3),\to(x+3,y-1),\to(x-4,y),\to(x,y-4),\to(x-1,y-1)\),发现过程中 \((x-y)\bmod 4\) 不变,且容易构造操作方案使得满足这一条件的变换必然能实现
然后考虑 \(C/D/E\) 部分,显然可以预先把 \(E\) 替换为 \(CD\)(操作同样可逆,因此对 \(s\) 和 \(t\) 都进行变换),令 \(x\) 为 \(C\) 的数量,\(y\) 为 \(D\) 的数量,则 \((x,y)\to(x-2,y-1),\to(x,y-3),\to(x,y+3),\to(x+2,y+1)\),发现 \((x-2y)\bmod 6\) 不变,且容易构造操作方案使得满足这一条件的变换必然能实现
综上,\(s\) 能变换为 \(t\) 当且仅当两个字符串内 \(A/B\) 只差对 \(4\) 同余,且 \(C/D/E\) 的加权和(\(C\) 权值为 \(1\),\(D\) 权值为 \(-2\),\(E\) 权值为 \(-1\))对 \(6\) 同余
容易做到 \(O(|s|+|t|+q)\)
代码:
#include <bits/stdc++.h>
using namespace std;
string s, t; int n, m, q;
int cs[100010], ct[100010];
int ccs[100010], cct[100010];
int main(){
freopen("change.in", "r", stdin);
freopen("change.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> s >> t >> q; n = s.size(), m = t.size(); s = " " + s, t = " " + t;
for (int i = 1; i <= n; ++i)
cs[i] = cs[i - 1] + (s[i] == 'A'? 1 : s[i] == 'B'? -1 : 0), ccs[i] = ccs[i - 1] + (s[i] == 'C'? 1 : s[i] == 'D'? -2 : s[i] == 'E'? -1 : 0);
for (int i = 1; i <= m; ++i)
ct[i] = ct[i - 1] + (t[i] == 'A'? 1 : t[i] == 'B'? -1 : 0), cct[i] = cct[i - 1] + (t[i] == 'C'? 1 : t[i] == 'D'? -2 : t[i] == 'E'? -1 : 0);
while (q--){
int l, r, L, R;
cin >> l >> r >> L >> R;
int s = cs[r] - cs[l - 1], t = ct[R] - ct[L - 1];
if (s - t & 3){cout << "NO" << "\n";continue;}
s = ccs[r] - ccs[l - 1], t = cct[R] - cct[L - 1];
if ((s - t) % 6)cout << "NO" << "\n";
else cout << "YES" << "\n";
}
return 0;
}
T2 旅途(travel)\(\quad\) UOJ #84. 【UR #7】水题走四方
题意
给定一棵树,两个物体初始在根,可以选择一个移动一步(用时一秒,两者可同时移动)或令一个物体瞬移到另一个物体处(不消耗时间),求遍历整棵树的最小用时,\(n\le5\times10^6\)
分析
显然存在一种方式使得其中只有一个物品进行瞬移,称其为乙,另一个称为甲
显然可以视为有以下操作:甲移动,乙移动,乙瞬移到甲,甲带着乙移动
显然从根出发甲只会向下移动,否则一定可以调整之
显然最优方案为:在树上选择一条从根出发的直链,两点沿着直链移动,链上有若干关键点,到达一关键点时,乙遍历当前关键点到下一个关键点之间分叉出去的子树
令 \({dp}_u\) 表示根到 \(u\) 的链上的答案,则最终答案为 \(\sum_{u\text{ is leaf}} dp_u\),初始 \(dp_1=0\),令 \(d_u\) 表示 \(u\) 的深度,令 \(d^s_u\) 表示子树 \(u\) 内叶子深度之和,令 \(n^l_u\) 表示子树 \(u\) 内叶子数量,令 $d^m(u,v)=\max_{w\mid w\notin\text{path}(u,v),{fa}_w\in\text{path}(u,v),fa_w\ne u} d_w $,转移为
其中 \((d^s_v-d^s_u)-(n^l_v-n^l_u)d^s_v\) 表示乙遍历两点之间分叉出去的子树,\(\max(0,d_u-d^m(u,v))\) 表示乙最后一次从 \(v\) 出发遍历 \(u-v\) 之间最远的一个叶子时,甲同步出发向下走到 \(u\) 后所需等待的时间
暴力实现容易做到 \(O(n^2)\),考虑优化
若产生 \(d_u>d^m(u,v)\) 的情况:
- 若 \(u\) 的父亲只有 \(u\) 一个儿子,则转移 \({dp}_u\gets {dp}_{fa_u}+1\),显然包含以上情况
- 否则不妨令 \(fa_u\) 也是关键点
由此可以简化为
对于第二种转移,只需要转移链上第一个满足要求的点即可,可证其它情况不优
因此维护根到 \(u\) 的关于 \(d^m(u,v)\) 的单调栈,转移时在栈上二分
注意栈是均摊的,因此插入新元素时弹出的位置也需要二分
容易做到 \(O(n\log n)\),需要使用非递归式写法以减小常数
存在 \(O(n)\) 算法
T3 花园(garden)
比赛结果
\(100+20+20\),\(\text{rk}-2\)

浙公网安备 33010602011771号