洛谷P4248 [AHOI2013]差异(后缀自动机求lcp之和)
题解:首先所有后缀都在最后一个np节点,然后他们都是从1号点出发沿一些字符边到达这个点的,所以下文称1号点为根节点,我们思考一下什么时候会产生lcp,显然是当他们从根节点开始一直跳相同节点的时候,所以思路就是先找出每个节点被几个后缀经过,这显然把边反转倒着找就可以了,然后他会被出现次数sz个串经过。
出现次数等于parent树子树中np类节点的个数,这跑个dfs就好了,一个相同前缀产生的贡献是sz*(sz-1)/2
然后思考一个点可能代表多个子串,但是他们的出现次数都是相同的,所以单个点的贡献为上面的单个贡献再乘上一个有几个子串
子串的个数为parent树父亲节点的最大长度减去该节点的最大长度
这样子在从根开始dfs,如果经过某个点只有一个后缀经过,就说明lcp结束了,就不用再搜该点了。
上面就求出了lcp的和
至于前面那个式子,只需要打个表找个规律发现是(n-1)*n*(n+1)/2就可以了
虽然常数大点但是还是后缀自动机复杂度的
但其实不用这么复杂,只要翻过来就可以建出原串后缀树,lcp就是后缀树的两个节点的lca,跑个树形dp就可以了。
代码因为没用链式前向星存边所以不开o2会t,但还是贴一下吧
- #include<bits/stdc++.h>
- #define N 1000010
- using namespace std;
- int n;
- int gg=;
- struct SAM
- {
- struct point
- {
- int son[],fa,len,mx;
- }t[N];
- int cnt=,last=;
- int f[N],sz[N];
- bool vis[N];
- vector<int> g[N],e[N];
- long long lcp=0ll;
- void add(int c)
- {
- int p=last;
- int np=++cnt;
- t[np].len=t[p].len+;
- sz[np]=;
- while(p&&(!t[p].son[c]))
- {
- t[p].son[c]=np;
- p=t[p].fa;
- }
- if(!p) t[np].fa=;
- else
- {
- int q=t[p].son[c],nq;
- if(t[p].len+==t[q].len)
- {
- t[np].fa=q;
- }
- else
- {
- nq=++cnt;
- t[nq]=t[q];
- t[nq].len=t[p].len+;
- t[q].fa=t[np].fa=nq;
- while(p&&(t[p].son[c]==q))
- {
- t[p].son[c]=nq;
- p=t[p].fa;
- }
- }
- }
- last=np;
- }
- void dfs(int now)
- {
- t[now].mx=t[now].len-t[t[now].fa].len;
- for(int i=;i<;i++)
- {
- if(t[now].son[i]) e[t[now].son[i]].push_back(now);
- }
- for(int i=;i<g[now].size();i++)
- {
- dfs(g[now][i]);
- sz[now]+=sz[g[now][i]];
- }
- }
- void dfs1(int now)
- {
- vis[now]=;
- for(int i=;i<e[now].size();i++)
- {
- f[e[now][i]]++;
- if(!vis[e[now][i]])
- {
- dfs1(e[now][i]);
- }
- }
- }
- void dfs3(int now)
- {
- vis[now]=;
- if(f[now]) lcp+=t[now].mx*(1ll*sz[now]*(sz[now]-)/);
- for(int i=;i<;i++)
- {
- if(f[t[now].son[i]]&&sz[t[now].son[i]]>&&(!vis[t[now].son[i]]))
- {
- dfs3(t[now].son[i]);
- }
- }
- }
- void solve()
- {
- for(int i=;i<=cnt;i++) g[t[i].fa].push_back(i);
- dfs();
- sz[]=;
- memset(vis,,sizeof(vis));
- dfs1(last);
- memset(vis,,sizeof(vis));
- dfs3();
- long long len=1ll*n*(n-)*(n+)/;
- printf("%lld\n",len-*lcp);
- }
- }sam;
- char s[];
- int main()
- {
- scanf("%s",s);
- n=strlen(s);
- for(int i=;i<n;i++)
- {
- sam.add(s[i]-'a');
- }
- sam.solve();
- }
洛谷P4248 [AHOI2013]差异(后缀自动机求lcp之和)的更多相关文章
- [洛谷P4248][AHOI2013]差异
题目大意:给一个长度为$n$的字符串,求: $$\sum\limits_{1\leqslant i<j\leqslant n}|suf_i|+|suf_j|-2\times lcp(suf_i, ...
- BZOJ 3238: [Ahoi2013]差异 [后缀自动机]
3238: [Ahoi2013]差异 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2512 Solved: 1140[Submit][Status ...
- [AHOI2013]差异 后缀自动机_Parent树
题中要求: $\sum_{1\leqslant i < j \leq n } Len(T_{i}) +Len(T_{j})-2LCP(T_{i},T_{j})$ 公式左边的部分很好求,是一个常量 ...
- [Ahoi2013]差异(后缀自动机)
/* 前面的那一坨是可以O1计算的 后面那个显然后缀数组单调栈比较好写??? 两个后缀的lcp长度相当于他们在后缀树上的lca的深度 那么我们就能够反向用后缀自动机构造出后缀树然后统计每个点作为lca ...
- [bzoj3238][Ahoi2013]差异——后缀自动机
Brief Description Algorithm Design 下面给出后缀自动机的一个性质: 两个子串的最长公共后缀,位于这两个串对应的状态在parent树上的lca状态上.并且最长公共后缀的 ...
- BZOJ 3238 [Ahoi2013]差异 ——后缀自动机
后缀自动机的parent树就是反串的后缀树. 所以只需要反向构建出后缀树,就可以乱搞了. #include <cstdio> #include <cstring> #inclu ...
- 洛谷4248 AHOI2013差异 (后缀数组SA+单调栈)
补博客! 首先我们观察题目中给的那个求\(ans\)的方法,其实前两项没什么用处,直接\(for\)一遍就求得了 for (int i=1;i<=n;i++) ans=ans+i*(n-1); ...
- BZOJ.3238.[AHOI2013]差异(后缀自动机 树形DP/后缀数组 单调栈)
题目链接 \(Description\) \(Solution\) len(Ti)+len(Tj)可以直接算出来,每个小于n的长度会被计算n-1次. \[\sum_{i=1}^n\sum_{j=i+1 ...
- BZOJ3238: [Ahoi2013]差异(后缀自动机)
题意 题目链接 Sol 前面的可以直接算 然后原串翻转过来,这时候变成了求任意两个前缀的最长公共后缀,显然这个值应该是\(len[lca]\),求出\(siz\)乱搞一下 #include<bi ...
随机推荐
- go 第一个项目
官方下载go: https://golang.org/dl/ 安装完成后:cmd命令下:go go env:查看当前的环境配置:
- Mybatis-Generator自动生成Dao、Model、Mapping等相关映射文件(懒人版)
今天在学习mybatis生成相关的映射文件的时候,发现了往期的生成Dao.Model.Mapping等文章多数都是一样的,我也在学着重复造轮子,不过是懒人造的.本文旨在解决开发过程,简化配置文件的“手 ...
- java.lang.IllegalArgumentException: Service not registered
java.lang.IllegalArgumentException: Service not registered 首先检查一下,Service是否在AndroidManifest文件中注册.格式如 ...
- Python3 bytes 函数
Python3 bytes 函数 Python3 内置函数 描述 bytes 函数返回一个新的 bytes 对象,该对象是一个 0 <= x < 256 区间内的整数不可变序列.它是 b ...
- Codeforces Round #533 (Div. 2)
C: 题意: 有n个整数ai,数列a有两个神奇的性质.1.所有的整数都在[l,r]范围内.2.这n个数的和能被3整除.现在给出l和r,和个数n,问你有多少种方法构造出数列a,方案数mod1e9+7. ...
- C++ 内存解析
一.内存基本构成可编程内存在基本上分为这样的几大部分:静态存储区.堆区和栈区.他们的功能不同,对他们使用方式也就不同. 静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在 ...
- php iframe 上传文件
我们通过动态的创建iframe,修改form的target,来实现无跳转的文件上传. 具体的实现步骤 1.捕捉表单提交事件 2.创建一个iframe 3.修改表单的target,指向iframe ...
- Vue.js 与 Laravel 分离
首先表示折腾了十来天的php-laravel框架和vue的结合开发又偏前端实在是太大的阻碍,首先laravel的机制就是写完路由router再加载blade模板的.如果要在laravel工程里面加载一 ...
- Zookeeper 系列(一)基本概念
Zookeeper 系列(一)基本概念 https://www.cnblogs.com/wuxl360/p/5817471.html 一.分布式协调技术 在给大家介绍 ZooKeeper 之前先来给大 ...
- 2018上IEC计算机高级语言(C)作业 第2次作业
2018上IEC计算机高级语言(C)作业 第2次作业 一.例程调试(20分) 调试下面1个例程,各位同学调试用自己的学号模4加1序号的题,写清错误提示(截小图)及修改内容(10分); 说明:有可能没有 ...