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 $,转移为

\[{dp}_u\gets \min_{v\text{ is ancestor of }u}\left({dp}_v+(d^s_v-d^s_u)-(n^l_v-n^l_u)d^s_v+\max(0,d_u-d^m(u,v))\right) \]

其中 \((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\) 也是关键点

由此可以简化为

\[{dp}_u\gets {dp}_{fa_u}+1\;\;(\text{if}~|son({fa}_u)|=1) \]

\[{dp}_u\gets \min_{v\text{ is ancestor of }u,\;d_u\ge d^m(u,v)}\left({dp}_v+(d^s_v-d^s_u)-(n^l_v-n^l_u)d^s_v\right) \]

对于第二种转移,只需要转移链上第一个满足要求的点即可,可证其它情况不优

因此维护根到 \(u\) 的关于 \(d^m(u,v)\) 的单调栈,转移时在栈上二分

注意栈是均摊的,因此插入新元素时弹出的位置也需要二分

容易做到 \(O(n\log n)\),需要使用非递归式写法以减小常数

代码

参考

存在 \(O(n)\) 算法

T3 花园(garden)

比赛结果

\(100+20+20\)\(\text{rk}-2\)

posted @ 2025-07-10 08:10  Hstry  阅读(21)  评论(0)    收藏  举报