当前位置: 首页 > news >正文

OI 杂讲

OI 杂讲

  • 字符串
    • 括号匹配
          • 例 1:
  • 图论
    • 最小生成树
      • kruskal
          • 例 1:P8207 [THUPC2022 初赛] 最小公倍树
      • prim
    • 连通性
      • tarjan
        • 点双
        • 边双
        • 强联通

与之前的类似,就是讲一点技巧,但是比较乱,凑合着看吧。

字符串

括号匹配

  • 几何意义:考虑令 (+1+1+1 变换,令 )−1-11 变换,然后对这个 +1/−1+1/-1+1/1 构成的变换序列在平面直角坐标系中变成一张从原点出发的折线图,也就是令 ( 为上斜坡,) 为下斜坡。
  • 如何判断合法:转化完之后,我们只需要判断构成折线是否仅在 xxx 轴或 yyy 轴或第一象限内且是否最终回到 000
  • 扩展:如果不是从原点开始,令开始时 yyy 坐标为 kkk,则判断每个位置是否都 ≥k\ge kk 且最终回到 kkk。这可以用来判断子串是否为合法括号序列。
例 1:

给定一个合法括号序列 SSS
定义好的序列为由 ?() 构成的序列(可以不包含其中任意数量个),且存在唯一一种方式将每个 ? 依次替换为 () 后,得到合法括号序列。
问如果每个位置都有 12\frac{1}{2}21 的概率变为 ?,那么有多大的概率变为好的序列。
998244353998244353998244353 取模,∣S∣≤106|S|\le10^6S106

做法:

  1. 先将概率转化成方案数。
  2. 注意到对于一个好的序列,直接把它变为输入的序列一定是一种方案,于是变为怎样指定某些位置翻转但是每一个位置都不能翻转的方案数。
  3. 考虑转化成几何意义后怎么做,那就变成了考虑什么时候某个位置可以翻转但是翻转后一定无法得到合法括号序列。
  4. 上述情况当且仅当:
    1. 不匹配,也就是如果翻转那么一定无法使得左右括号数量相同,这会导致无法回到 000,出现情况当且仅当对于 (,后面不存在一个位置 ) 也可以被翻转,或对于 ),前面不存在 ( 也可以被翻转。(注意到一旦存在就一定会贡献给某个异类括号,因此即使不贡献给枚举的位置也会导致无解)。
    2. 翻转之后跑到 xxx 轴下面去了,这当且仅当翻转的位置为 ( 且直到下一个可以被翻转的 ( 前存在某个位置的原本的 yyy 坐标 ≤1\le 11
  5. 考虑对这个怎么计数,首先我们能得到一些结论:
    1. 不会存在 ...)...(... 然后两个括号都变成 ?,因为他们既可以跟原来的括号匹配,又可以翻转过来,跟内部的匹配或是跟对方匹配,可以证明这两种方案之一一定合法,转化成折线图后易证。也就是说,一定是一些 ( 被选择,然后一些 ) 被选择。
    2. 一个 (可以被标记为 ? 当且仅当以下条件之一成立:
      1. 它到后一个可以被翻转的 ) 中存在某个位置的 yyy 坐标 ≤1\le 11,翻转后会使得这个点坐标 −2-22,于是不合法,于是乎可以标记。
      2. 它后面没有标记为 ?) 了,这样如果翻转它一定回不到 000
  6. 于是什么时候可以放 (,什么时候可以放 ) 都已经确定好了,那么直接计数即可,我的做法是先存储 yyy 坐标 =0=0=0 的的配对的括号,枚举第一个 ) 被选择的区间,计算它的贡献,然后再在里面找坐标 =1=1=1 的括号,然后对于这样的一个位置,计算它的贡献。
  7. 具体来说,y=0y=0y=0 的贡献是左端点随便取或不取,然后如果这个区间里面选择取 (,那么除掉最后一个 ) 以外的所有 ) 都不能取(我们先没考虑 y=1y=1y=1 的影响),最后一个 ) 必须取。
  8. 接着考虑,y=1y=1y=1,那么它包含的前面一段 ( 选择后,这段的 ) 不能选,由于是计算比 y=0y=0y=0 更多的贡献,所以不计算选 ) 和部分选 ( 的情况以免算重,我们只需要计算不会被 y=0y=0y=0 包含的部分,也就是这个位置之后可以有 ) 被选择,加入答案即可。

实现可能较为困难,读者不妨尝试。原题是这个比赛的 T2,作者从 8:40 到 19:00 断断续续写了至少 7 小时才 A 掉。

总结:这题本质上是在考虑处理第一个 ) 被选择的位置,考虑不同位置会有什么影响,然后按照 y≤1y\le 1y1 来分段处理。

图论

最小生成树

kruskal

kruskal 的核心思想是每次贪心加能合并两个联通快的边中边权最小的边。

正确性:考虑反证法:假设我们有一次没有选择最小的边,假设这条边为 (u,v)(u,v)(u,v),那么 uuu 所在的联通块最终还是要和 vvv 所在的联通块合并,而之后合并代价一定不小于这次合并,于是替换为这次合并一定不劣,证毕。

例 1:P8207 [THUPC2022 初赛] 最小公倍树

不知道问什么这题只有蓝。感觉要对 kruskal 有很深入的理解才能解决,严格难于 NOI 中的那个归程吧(尽管那题要 kruskal生成树,但是这个东西也不难理解吧)

做法:

  1. 看到 lcm\text{lcm}lcm,容易想到去拆开成 lcm(x,y)=x×ygcd⁡(x,y)\text{lcm}(x,y)=\frac{x\times y}{\gcd(x,y)}lcm(x,y)=gcd(x,y)x×y,然后贪心的想让他们的 gcd⁡\gcdgcd 尽可能大。
  2. 如果 gcd⁡(x,y)≥L\gcd(x,y)\ge Lgcd(x,y)L,那么 xxxyyy 连边一定仅会连向 gcd⁡(x,y)\gcd(x,y)gcd(x,y)(但不一定会连,也就是不一定是 xxxyyy 位置来合并)
  3. 考虑 gcd⁡(x,y)≤L\gcd(x,y)\le Lgcd(x,y)L 的情况,容易去想贪心,也就是取一个最小的 zzz 满足 z≥Lz\ge LzLgcd⁡(x,y)∣x\gcd(x,y)|xgcd(x,y)x。考虑为什么 xxxyyy 一定不会向除了 zzz 外的点连边来合并这两个联通快。
  4. 这并不显然,但是是对的。考虑 kruskal 的本质,他就是一次次加最小的边,于是我们只需要证明加任何若干条边合并 xxxyyy 联通快的代价一定大于它。注意到 gcd⁡(x,y)≤L\gcd(x,y)\le Lgcd(x,y)L,所以这个区间内所有的数都不可能同时是 xxxyyy 的因数,我们考虑取 p=a×gcd⁡(x,y)p=a\times\gcd(x,y)p=a×gcd(x,y),由于上述结论,xxxyyy 中只能有一个数满足 gcd⁡(x,a)≠1\gcd(x,a)\ne 1gcd(x,a)=1,令这个数为 xxx(如果不存在显然连向 zzz 更优),注意到 xxx 连向 zzz 的代价比 yyy 连向 ppp 的代价更小,于是在 xxx 连向 zzz 时他们就已经联通(yyy 肯定更先连向 zzz)。
  5. 那么就很显然了,我们直接枚举 d=gcd⁡(x,y)d=\gcd(x,y)d=gcd(x,y),然后得到它第一个 ≥L\ge LL 的倍数 zzz,让 zzz 连向这个区间内所有 ddd 的倍数即可。
  6. 考虑分析复杂度,边数 ∑d=1RR−L+1d\sum_{d=1}^R \frac{R-L+1}{d}d=1RdRL+1O(nlog⁡n)O(n\log n)O(nlogn) 级别的(我们令 n=R−L+1n=R-L+1n=RL+1),对它们排序是 O((nlog⁡n)log⁡(nlog⁡n))O((n\log n)\log(n\log n))O((nlogn)log(nlogn)) 级别的,也就是O(nlog⁡2n+nlog⁡nlog⁡log⁡n)=O(nlog⁡2n)O(n\log^2 n+n\log n\log\log n)=O(n\log^2 n)O(nlog2n+nlognloglogn)=O(nlog2n)。可以通过。

总结:这题的思想是贪心,并运用 kruskal 的性质去证明贪心正确,需要对 kruskal 有不浅的了解,洛谷题解区的证明大多都是伪证,当然我没说我的证明就严谨了,只是个人觉得好一点而已。

prim

prim 的核心与 kruskal 不同,它的核心是加点,考虑最小生成树的某个一个已经确定了点和边的子集 TTT,它的结论是每次新加入的点是这些点能到达的点的中代价最小的。

正确性:首先这里的证明基于无向图。首先选择的一定是能到达的点中的点,否则无法联通。那么就是证为什么选最小的最优,假设你没有选最小代价的点 uuu,那么 uuu 最终还是要并到 TTT 中的,如果只是后选择 u→Tu\to TuT 的边没有本质区别,于是一定是经过了一串点到达了某个连向 TTT 的点 vvvv→Tv\to TvT 的代价不小于于 u→Tu\to TuT 的代价,于是如果你反过来,让 vvv 经过同一串点到达 uuu,再并到 TTT,一定不劣,于是假设不成立,证毕。

好像之前做过一题需要理解 prim 思想的,但是忘了,回学校问问同学和老师看有没有记得的,如果有,就补上。

连通性

tarjan

tarjan 的思想就是树上 dp,没必要记具体怎么写,场上推推就可以得到了。细节比较多,记错就坏事了。下面讲讲怎么推。

点双

首先无向图 dfs 树有个性质就是没有横跨边,只有返祖边,其实有也没事,毕竟在无向图中返祖边和横跨边的贡献是相同的。

考虑如果我们确定了根,什么时候会出现点双。考虑对于一个节点 uuu,它不会成为割点当且仅当它的子树内所有点都可以在不经过 uuu 的前提下回退到它之前的某个位置。反之,如果有任意一个点在不经过 uuu 的前提下无法回退到 uuu 以前,uuu 就是割点。

由于这是无向图,如果 uuu 的所有儿子 vvv 的子树内都存在一条边能连向 uuu 子树外,那么uuu 子树外的点原先就是连通的,那么所有点都能进入 uuu 子树内,于是随意指定一个根都能得到正确的割点。

考虑如何求一个点子树内的点能到达的最小 dfs 序是多少,记 uuu 的 dfs 序为 dfnudfn_udfnu,记子树内能到达的最小 dfs 序为 lowulow_ulowu(沿用了大众命名方式)。接下来我们将分别讨论树边、返祖边、前向边的贡献。记这条边为 (u,v)(u,v)(u,v)

  • 树边:显然当你试图割掉当前考虑节点 uuu 的祖先时,并不会影响到树边,所以你直接令 lowu←min⁡(lowu,lowv)low_u\gets\min(low_u,low_v)lowumin(lowu,lowv)
  • 返祖边:当你考虑断掉 vvv 的时候,你会发现这条边不能跳出去了,也就是说,返祖边会被删除 vvv 时候影响到你能否走,按道理你应该存储 lowu,klow_{u,k}lowu,k 表示 uuu 的子树内不走 kkk 节点能回到的最小 dfs 序,但是事实上,对于在 vvvuuu 包括 uuu 的点,lowu←min⁡(lowu,lowv)low_u\gets\min(low_u,low_v)lowumin(lowu,lowv)lowu←min⁡(lowu,dfnv)low_u\gets\min(low_u,dfn_v)lowumin(lowu,dfnv) 并无差别,而 vvv 节点则只有 lowu←min⁡(lowu,dfnv)low_u\gets\min(low_u,dfn_v)lowumin(lowu,dfnv) 才是对的,再往上理论上只有 lowu←min⁡(lowu,lowv)low_u\gets\min(low_u,low_v)lowumin(lowu,lowv) 是对的,但是注意到,再往上 vvv 节点的 lowlowlow 已经会被树边考虑并计算,所以并无差别。所以一般来说,我们会选择写 lowu←min⁡(lowu,dfnv)low_u\gets\min(low_u,dfn_v)lowumin(lowu,dfnv)。这样就不用考虑会不会被 vvv 断掉的影响了。
  • 前向边:没有影响。

这样你也就发现了,其实我们求出的 lowlowlow 与我们定义的 lowlowlow 并不相同,仅仅能帮助我们求出割点而已。其实作者原本是想用语文的方式来表达 lowlowlow 的真正含义,但是原谅作者语文不好。

对了,求出来割点可以钦定割点不选然后 dfs 得到点双,这样好写一点,但是据我们老师说常数大。

也可以考虑我们找到一个割点就把它割掉,然后取出它的子树。用我们老师的话说,就是一次次割掉叶子。具体实现往往是用栈,这可以去看洛谷题解。

边双

点双我们考虑的是断掉点影不影响连通性,而边双我们考虑的是断掉树边影不影响连通性(别的边肯定能不会影响连通性)。

类似的,我们考虑三种边的贡献,树边自然没有区别,前向边仍然不会产生贡献,我们要考虑的只有返祖边。

这里有点区别的是,你在考虑返祖边 (u,v)(u,v)(u,v) 时,vvv 断掉向下的树边并不会影响到这条返祖边,但是经过我们上述分析,你写 lowu←min⁡(lowu,dfnv)low_u\gets\min(low_u,dfn_v)lowumin(lowu,dfnv)lowu←min⁡(lowu,lowv)low_u\gets\min(low_u,low_v)lowumin(lowu,lowv) 都可以。为了统一,我更喜欢写 lowu←min⁡(lowu,dfnv)low_u\gets\min(low_u,dfn_v)lowumin(lowu,dfnv)

求边双和求点双差不多,同样可以 dfs 或用栈来处理。

强联通
http://www.dtcms.com/a/305143.html

相关文章:

  • ASDIP Concrete(混凝土结构设计软件) v6.0.0.2 免费版
  • 光环云 × 零一万物在上海WAIC联合发布“法律智算综合云服务”,以专业Agent助力法律普惠发展
  • debug redis里面的lua脚本
  • JSON在java中的使用
  • c++之链表
  • 技术干货 | 矢网DTF测量技术:透视线缆、天线与波导内部缺陷的“射频X光”(二)
  • 人工智能赋能社会治理:深度解析与未来展望
  • 移位运算以及定点数的加减法操作
  • 深入解析 Spring SpEL:SpelExpressionParser 的使用与实践
  • Python游戏开发:Pygame全面指南与实战
  • JAVA存储原生json字符串到redis,去除@class,实现原生命令操作教程
  • 从传统到智能:Midscene.js 如何用 AI 颠覆自动化测试!
  • 【Lua】题目小练4
  • 深入解析RocksDB的MVCC和LSM Tree level
  • 基于springboot/java/VUE的旅游管理系统/旅游网站的设计与实现
  • USB Type-C PD协议一文通
  • mangoDB面试题及详细答案 117道(026-050)
  • CVE-2021-1675
  • 【C语言进阶】题目练习
  • docker部署zingerbee/netop 轻量级网络流量监控工具
  • 河南萌新联赛2025第(二)场:河南农业大学(补题)
  • 高端医疗超声AFE模拟前端应用
  • 机器学习之线性回归——小白教学
  • 关于为什么写分配法搭配写回法?非写分配法搭配全写法?
  • python基础:request请求查询参数的基本使用、携带请求参数的两种方法、 json串和python中数据类型转化、 post模拟登录
  • 全方位Python学习方法论:从入门到精通的系统指南
  • GB/T 4706.1-2024 家用和类似用途电器的安全 第1部分:通用要求 与2005版差异(21)
  • 【Spring】日志级别的分类和使用
  • 计算机视觉-局部图像描述子
  • 代理IP轮换机制:突破反爬虫的关键策略