串串划分

题目链接:UOJ 429

题目大意

给你一个字符串,然后问你有多少个划分,满足相邻的两个串不相等,且每个串都不存在整周期。

思路

Lyndon Tree

L y n d o n T r e e \rm Lyndon\ Tree Lyndon Tree 是什么呢?
它就是你不是可以通过把两个串 L y \rm Ly Ly 拼起来得到新的 L y \rm Ly Ly 吗,那我们对于一个串找它最小的后缀(不能算它自己),然后得到两个串,然后一直划分下去,就得到了 L y n d o n T r e e \rm Lyndon\ Tree Lyndon Tree。

然后这个东西是建立在这个串是 L y \rm Ly Ly 的基础上的,如果不是的话就是对于 L y \rm Ly Ly 划分的每个串都这么弄,形成一个森林。

然后你思考一下会发现它其实是 S A \rm SA SA 的 r a n k \rm rank rank 数组建笛卡尔树得到的树。

然后有一些性质:
如果 [ l , r ] [l,r] [l,r] 是 L y \rm Ly Ly,那 l , r l,r l,r 字符对应点在树上的 L C A \rm LCA LCA 是 a a a(设),对于子串 [ l a , r a ] [l_a,r_a] [la​,ra​],那会有 l = l a ⩽ r a ⩽ r l=l_a\leqslant r_a\leqslant r l=la​⩽ra​⩽r。
然后如果这个区间是 L y s , f ( l ) \rm {Ly}_{s,f}(l) Lys,f​(l)(不难感受到这个树也是分 f f f 的),那一定是在右儿子(因为是最长的 L y \rm Ly Ly 啊)
然后差不多的道理,任意一个 r u n \rm run run 的 L y R \rm LyR LyR 一定会在右儿子出现(这个就在 f ∈ { 0 , 1 } f\in\{0,1\} f∈{0,1} 的其中一个),然后好像是看那个满足 s r + 1 < f s r + 1 − p s_{r+1}<_fs_{r+1-p} sr+1​<f​sr+1−p​


似乎之前没有说过 W P L \rm WPL WPL 和 P L \rm PL PL,这里提一句把。

对于一个串 S S S 有 p , q p,q p,q 两个周期,如果 p + q ⩽ ∣ S ∣ p+q\leqslant |S| p+q⩽∣S∣,那么还有 gcd ⁡ ( p , q ) \gcd(p,q) gcd(p,q) 的周期( W P L \rm WPL WPL, W e a k P e r i o d i c i t y L e m m a \rm Weak\ Periodicity\ Lemma Weak Periodicity Lemma)
(这个你把一个位置用两个周期移一下可以得到减的位置,然后辗转相除)

然后其实可以扩展到 p + q − gcd ⁡ ( p , q ) ⩽ ∣ S ∣ p+q-\gcd(p,q)\leqslant |S| p+q−gcd(p,q)⩽∣S∣,就是 P L \rm PL PL, P e r i o d i c i t y L e m m a \rm Periodicity Lemma PeriodicityLemma 了。
(不会证,好像可以先举例得到是上界,然后比例巴拉搞一通)


有什么用呢,可以搞一个叫做“子串半周期查询”的东西(Two-Period Queries)

这个东西是要你快速查询一个子串是否有不超过长度一半的周期,如果有输出最小的。

我们定义一个 e x r u n ( i , j ) {\rm exrun}(i,j) exrun(i,j) 为满足 i ′ ⩽ i , j ⩽ j ′ , p ⩽ ( j − i + 1 ) / 2 i'\leqslant i,j\leqslant j',p\leqslant(j-i+1)/2 i′⩽i,j⩽j′,p⩽(j−i+1)/2 的一个 r u n ( i ′ , j ′ , p ) \rm run(i',j',p) run(i′,j′,p)。
然后我们可以证出它如果存在就是唯一。
(因为如果不是唯一,重叠的那些可以用 W P L \rm WPL WPL 发现矛盾)

那这个定义就可以解决上面的问题,我们考虑对 f ∈ { 0 , 1 } f\in\{0,1\} f∈{0,1} 都建出 L y n d o n T r e e \rm{Lyndon\ Tree} Lyndon Tree,然后让 a = l c a ( [ i , ⌈ ( i + j ) / 2 ⌉ ] ) a={\rm lca}([i,\left\lceil(i+j)/2\right\rceil]) a=lca([i,⌈(i+j)/2⌉]),然后判断右儿子是不是满足条件就可以了。

至于有这个位置可以判就是因为一定有对应的 L y R \rm LyR LyR 包含 ⌈ ( i + j ) / 2 ⌉ \left\lceil(i+j)/2\right\rceil ⌈(i+j)/2⌉,那根据上面的性质,就会在某个点的右儿子位置出现。
然后我们会发现 a a a 的位置也会 > p >p >p,也包含那个位置,所以 a a a 会是这个 L y R \rm LyR LyR 的祖先。
那只要它右儿子不是那个 L y R \rm LyR LyR,那也会是它祖先。

那都是右儿子可以得到位置依次变大。
如果中间那个的 ⩽ j \leqslant j ⩽j,那它对应的位置有 p p p 这个周期,那它就不是 L y \rm Ly Ly 了,矛盾。
如果 > j >j >j,那个 L y R \rm LyR LyR 的左边界跟它的右边界形成的区间会小于它形成的区间,那也跟它是 L y \rm Ly Ly 矛盾了(定义上)。
所以就反证了。

这道题

这道题还是很友好滴!
(至少不用树状数组)

考虑找不合法的是怎样,看到划分考虑给每一种划分出的字符串一个函数,然后一个划分的权值就是所有的乘起来(这样出现不合法的就直接给 0 0 0)
然后发现如果只是要没有整周期就可以弄,但是有说两个之间不能是相同的。

考虑到这个相同的很相似,你如果把两个相同的并起来就形成了前面的的整周期。
所以你考虑容斥掉,就整周期的情况跟两个之间的抵消掉。
然后思考一下不难发现容斥系数就是这个串它最小循环节的出现次数。

那考虑 DP,不难得到 n 2 n^2 n2 的形式(枚举新的子串是从哪里开始),考虑优化。
那首先特别的地方是有整周期的,那别的我们就可以直接前缀和,然后减去有整周期的。

然后考虑整周期,首先把 r u n \rm run run 都找出来,那我们就可以得到一些区间。
发现对于一个 r u n \rm run run, r r r 它能提供给一个点能从转移的位置是一个等差序列。
那我们只需要对于每个 r r r 维护当前出现的每个等差序列它们出现的点的贡献和即可。
然后注意到出现一个点,那别的之前的点的循环节出现次数 + 1 +1 +1(亦或者说全部加一,最后这个从 0 0 0 到 1 1 1),然后你取反一下然后加即可。

代码

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define ll long long
#define mo 998244353
#define ull unsigned long longusing namespace std;const int N = 2e5 + 100;
char s[N];
int n, ly[N], m;
struct RUNS {int x, y, p;
}runs[N << 1];
vector <int> ed[N];
vector <ll> sum[N << 1][2];
//0:减去前缀和
//1:容斥
ll f[N];struct HASH {const int di = 131;ull mi[N], hash[N];void Init() {mi[0] = 1;for (int i = 1; i <= n; i++) {mi[i] = mi[i - 1] * di;hash[i] = hash[i - 1] * di + s[i] - 'a' + 1;}}ull get(int x, int y) {return hash[y] - hash[x - 1] * mi[y - x + 1];}
}H;int getl(int x, int y) {int l = 1, r = min(x, y), re = 0;while (l <= r) {int mid = (l + r) >> 1;if (H.get(x - mid + 1, x) == H.get(y - mid + 1, y)) re = mid, l = mid + 1;else r = mid - 1;}return re;
}int getr(int x, int y) {int l = 1, r = min(n - x + 1, n - y + 1), re = 0;while (l <= r) {int mid = (l + r) >> 1;if (H.get(x, x + mid - 1) == H.get(y, y + mid - 1)) re = mid, l = mid + 1;else r = mid - 1;}return re;
}int sta[N];bool cmpS(int x, int y) {int pl = getr(x, y);return s[x + pl] < s[y + pl];
}void Lyndon(bool op) {sta[0] = 0; sta[++sta[0]] = n + 1; sta[++sta[0]] = n; ly[n] = n;for (int i = n - 1; i >= 1; i--) {while (sta[0] > 1 && cmpS(i, sta[sta[0]]) == op) sta[0]--;sta[++sta[0]] = i; ly[i] = sta[sta[0] - 1] - 1;}
}void check(int x, int y) {int l = getl(x, y), r = getr(x, y);if (l + r >= y - x + 1) runs[++m] = (RUNS){x - l + 1, y + r - 1, y - x};
}bool cmp_runs(RUNS x, RUNS y) {if (x.x != y.x) return x.x < y.x;if (x.y != y.y) return x.y < y.y;return x.p < y.p;
}void Init() {H.Init();for (int op = 0; op <= 1; op++) {Lyndon(op);for (int i = 1; i <= n; i++) {check(i, ly[i] + 1);} }sort(runs + 1, runs + m + 1, cmp_runs);int tmp = m; m = 0;for (int i = 1; i <= tmp; i++)if (runs[i].x != runs[i - 1].x || runs[i].y != runs[i - 1].y)runs[++m] = runs[i];
} int main() {scanf("%s", s + 1); n = strlen(s + 1);Init();for (int i = 1; i <= m; i++) {for (int r = runs[i].x + 2 * runs[i].p - 1; r <= runs[i].y; r++)ed[r].push_back(i);int sz = runs[i].p;if (runs[i].y - (runs[i].x + 2 * runs[i].p - 1) + 1 <= runs[i].p)sz = (runs[i].y - runs[i].x + 1) % runs[i].p;sz++;sum[i][0].resize(sz); sum[i][1].resize(sz);}f[0] = 1; ll Sum = 1;for (int i = 1; i <= n; i++) {f[i] = Sum;for (int j = 0; j < ed[i].size(); j++) { int v = ed[i][j];int l = runs[v].x, r = runs[v].y, p = runs[v].p;int id = (i - l + 1) % p;(sum[v][0][id] += mo - f[i - 2 * p]) %= mo;(sum[v][1][id] *= mo - 1) %= mo; (sum[v][1][id] += mo - f[i - 2 * p]) %= mo;(f[i] += sum[v][0][id] + sum[v][1][id]) %= mo;}(Sum += f[i]) %= mo;}printf("%lld", f[n]);return 0;
}

【UOJ 429】串串划分(Runs)(容斥)+ 有关 Lyndon Tree 及其应用的小小记录相关推荐

  1. UOJ #214 合唱队形 (概率期望计数、DP、Min-Max容斥)

    UOJ #214 合唱队形 (概率期望计数.DP.Min-Max容斥) 9个月的心头大恨终于切掉了!!!! 非常好的一道题,不知为何uoj上被点了70个差评. 题目链接: http://uoj.ac/ ...

  2. UOJ #214 [UNR #1]合唱队形 (概率期望计数、DP、Min-Max容斥)

    9个月的心头大恨终于切掉了!!!! 非常好的一道题,不知为何uoj上被点了70个差评. 题目链接: http://uoj.ac/problem/214 题目大意: 请自行阅读. 题解: 官方题解讲得相 ...

  3. UOJ#449. 【集训队作业2018】喂鸽子 min-max容斥,FFT

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ449.html 题解 设 f(i) 表示给 i 只鸽子喂食使得至少一只鸽子被喂饱的期望次数,先 min-max容斥 一下. ...

  4. UOJ #390 【UNR #3】百鸽笼 容斥+DP

    题目分析 算法0 每个管理员选哪一列,将构成一个长度为 N − 1 N-1 N−1的序列,序列的种数可以通过经典的将 a a a个相同元素插入到一个没有该元素的长度为 b b b的序列里问题,轻松求出 ...

  5. HDU - 7009 树上游走(树的直径+容斥)

    题目链接:点击查看 题目大意:给一棵树,称一个点集 S 是好的当且仅当存在一个点,其到 S 中所有点的距离互不相同,求 |S| 的最大值和使得 |S| 最大的 S 的个数 题目分析:不难看出 ∣S∣| ...

  6. 【XSY3657】因数分解(容斥,DP)

    考虑没有 bib_ibi​ 的限制怎么做. 先把 n!n!n! 质因数分解:n!=∏i=1kpiqin!=\prod\limits_{i=1}^kp_i^{q_i}n!=i=1∏k​piqi​​. 设 ...

  7. ARC101E Ribbons on Tree 树形dp 容斥

    题目链接 题意: 给你一棵nnn个点的树,nnn是偶数,把这些点分成n2\frac{n}{2}2n​个点对,每个点对会把路径上的所有边覆盖,问你每条边至少覆盖一次有多少种配对方式.n<=5000 ...

  8. 【BZOJ4455】小星星(动态规划,容斥)

    题面 BZOJ 洛谷 Uoj 题解 题意说简单点就是给定一张nnn个点的图和一棵n" role="presentation">nnn个点的树,现在要让图和树之间的点 ...

  9. [BZOJ4455][UOJ185][Zjoi2016]小星星(树形DP+容斥)

    将问题抽象化: 一个nnn个节点的树,和一个n" role="presentation" style="position: relative;"> ...

最新文章

  1. GMTC 大前端时代前端监控的最佳实践
  2. CVPR官网:仍将进行线下会议,网友:您老真在乎办会费
  3. 分子偶极矩大小如何判断_润滑油粘度大小该如何判断你知道吗?
  4. Linux下tomcat内存溢出分析及优化
  5. 测试思想-好东西与大家分享-1
  6. Pyhton学习——Day9
  7. vs2008 MFC访问Access 2010数据库
  8. 解决Secure Shell Client(SSH)客户端中文乱码的方法
  9. CVS常用命令速查手册
  10. 【毕设教程】如何使用单片机控制步进电机
  11. 【唐宇迪 深度学习-3D点云实战系列】学习笔记
  12. postgresql 数据库 update更新慢的原因(已解决)
  13. mysql 8.XXX zip版的安装使用
  14. LDAP认证的两种方式
  15. 【Qt】 Fractal Designer 4.6 - Help(中文版)
  16. 记录一次电脑数据删除分区后的恢复
  17. 抖音八大推流机制是怎样的?鹰迪电子商务
  18. 关于zip包的一点小东西 暴力 字典 明文
  19. Vue编译时写在style中的路径问题
  20. 全志A64的Android PC --Remix Mini

热门文章

  1. 微信支付java版v2.0_刷卡支付-翟东平-专题视频课程
  2. 修改VS code 界面字体
  3. 2020牛客暑期多校训练营(第十场) Hearthstone Battlegrounds
  4. 最便宜的html手机,最便宜骁龙865手机发布了,价格比你想象中的还低!
  5. 张春华现代教育计算机病毒是指,计春华
  6. echarts x轴字数溢出隐藏
  7. stringstream对char输出
  8. USB2.0 Camera 黄色叹号,不能用摄像头
  9. C语言程序设计 详细注意事项
  10. Android图片生成器