题目见此

题解:首先所有后缀都在最后一个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,但还是贴一下吧

  1. #include<bits/stdc++.h>
  2. #define N 1000010
  3. using namespace std;
  4.  
  5. int n;
  6. int gg=;
  7.  
  8. struct SAM
  9. {
  10. struct point
  11. {
  12. int son[],fa,len,mx;
  13. }t[N];
  14.  
  15. int cnt=,last=;
  16. int f[N],sz[N];
  17. bool vis[N];
  18. vector<int> g[N],e[N];
  19. long long lcp=0ll;
  20.  
  21. void add(int c)
  22. {
  23. int p=last;
  24. int np=++cnt;
  25. t[np].len=t[p].len+;
  26. sz[np]=;
  27. while(p&&(!t[p].son[c]))
  28. {
  29. t[p].son[c]=np;
  30. p=t[p].fa;
  31. }
  32. if(!p) t[np].fa=;
  33. else
  34. {
  35. int q=t[p].son[c],nq;
  36. if(t[p].len+==t[q].len)
  37. {
  38. t[np].fa=q;
  39. }
  40. else
  41. {
  42. nq=++cnt;
  43. t[nq]=t[q];
  44. t[nq].len=t[p].len+;
  45. t[q].fa=t[np].fa=nq;
  46. while(p&&(t[p].son[c]==q))
  47. {
  48. t[p].son[c]=nq;
  49. p=t[p].fa;
  50. }
  51. }
  52. }
  53. last=np;
  54. }
  55.  
  56. void dfs(int now)
  57. {
  58. t[now].mx=t[now].len-t[t[now].fa].len;
  59. for(int i=;i<;i++)
  60. {
  61. if(t[now].son[i]) e[t[now].son[i]].push_back(now);
  62. }
  63. for(int i=;i<g[now].size();i++)
  64. {
  65. dfs(g[now][i]);
  66. sz[now]+=sz[g[now][i]];
  67. }
  68. }
  69.  
  70. void dfs1(int now)
  71. {
  72. vis[now]=;
  73. for(int i=;i<e[now].size();i++)
  74. {
  75. f[e[now][i]]++;
  76. if(!vis[e[now][i]])
  77. {
  78. dfs1(e[now][i]);
  79. }
  80. }
  81. }
  82.  
  83. void dfs3(int now)
  84. {
  85. vis[now]=;
  86. if(f[now]) lcp+=t[now].mx*(1ll*sz[now]*(sz[now]-)/);
  87. for(int i=;i<;i++)
  88. {
  89. if(f[t[now].son[i]]&&sz[t[now].son[i]]>&&(!vis[t[now].son[i]]))
  90. {
  91. dfs3(t[now].son[i]);
  92. }
  93. }
  94. }
  95.  
  96. void solve()
  97. {
  98. for(int i=;i<=cnt;i++) g[t[i].fa].push_back(i);
  99. dfs();
  100. sz[]=;
  101. memset(vis,,sizeof(vis));
  102. dfs1(last);
  103. memset(vis,,sizeof(vis));
  104. dfs3();
  105. long long len=1ll*n*(n-)*(n+)/;
  106. printf("%lld\n",len-*lcp);
  107. }
  108.  
  109. }sam;
  110.  
  111. char s[];
  112.  
  113. int main()
  114. {
  115. scanf("%s",s);
  116. n=strlen(s);
  117. for(int i=;i<n;i++)
  118. {
  119. sam.add(s[i]-'a');
  120. }
  121. sam.solve();
  122. }

洛谷P4248 [AHOI2013]差异(后缀自动机求lcp之和)的更多相关文章

  1. [洛谷P4248][AHOI2013]差异

    题目大意:给一个长度为$n$的字符串,求: $$\sum\limits_{1\leqslant i<j\leqslant n}|suf_i|+|suf_j|-2\times lcp(suf_i, ...

  2. BZOJ 3238: [Ahoi2013]差异 [后缀自动机]

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2512  Solved: 1140[Submit][Status ...

  3. [AHOI2013]差异 后缀自动机_Parent树

    题中要求: $\sum_{1\leqslant i < j \leq n } Len(T_{i}) +Len(T_{j})-2LCP(T_{i},T_{j})$ 公式左边的部分很好求,是一个常量 ...

  4. [Ahoi2013]差异(后缀自动机)

    /* 前面的那一坨是可以O1计算的 后面那个显然后缀数组单调栈比较好写??? 两个后缀的lcp长度相当于他们在后缀树上的lca的深度 那么我们就能够反向用后缀自动机构造出后缀树然后统计每个点作为lca ...

  5. [bzoj3238][Ahoi2013]差异——后缀自动机

    Brief Description Algorithm Design 下面给出后缀自动机的一个性质: 两个子串的最长公共后缀,位于这两个串对应的状态在parent树上的lca状态上.并且最长公共后缀的 ...

  6. BZOJ 3238 [Ahoi2013]差异 ——后缀自动机

    后缀自动机的parent树就是反串的后缀树. 所以只需要反向构建出后缀树,就可以乱搞了. #include <cstdio> #include <cstring> #inclu ...

  7. 洛谷4248 AHOI2013差异 (后缀数组SA+单调栈)

    补博客! 首先我们观察题目中给的那个求\(ans\)的方法,其实前两项没什么用处,直接\(for\)一遍就求得了 for (int i=1;i<=n;i++) ans=ans+i*(n-1); ...

  8. BZOJ.3238.[AHOI2013]差异(后缀自动机 树形DP/后缀数组 单调栈)

    题目链接 \(Description\) \(Solution\) len(Ti)+len(Tj)可以直接算出来,每个小于n的长度会被计算n-1次. \[\sum_{i=1}^n\sum_{j=i+1 ...

  9. BZOJ3238: [Ahoi2013]差异(后缀自动机)

    题意 题目链接 Sol 前面的可以直接算 然后原串翻转过来,这时候变成了求任意两个前缀的最长公共后缀,显然这个值应该是\(len[lca]\),求出\(siz\)乱搞一下 #include<bi ...

随机推荐

  1. python 本地变量和全局变量 locals() globals() global nonlocal 闭包 以及和 scala 闭包的区别

    最近看 scala ,看到了它的作用域,特此回顾一下python的变量作用域问题. A = 10 B = 100 print A #10 print globals() #{'A': 10, 'B': ...

  2. Android中decode JPG时建议使用inPreferQualityOverSpeed

    在BitmapFactory.decodeBitmap方法中,参数BitmapFactory.Options里有一项是inPreferQualityOverSpeed:设为true的话,画质更好,加载 ...

  3. Gradle with Android

    [Gradle with Android] The Android Studio build system is based on Gradle, and the Android plugin for ...

  4. for循环计算阶乘

    int x = 10; for(int y = x - 1; y >= 1; y--) { x = x * y; } System.out.println(x); 从10乘到1的阶乘写法. lo ...

  5. Python学习day5作业

    目录 Python学习day5作业 ATM和购物商城 1. 程序说明 2. 基本流程图 3. 程序测试帐号 4. 程序结构: 5. 程序测试 title: Python学习day5作业 tags: p ...

  6. MongoDB使用场景和局限 (转)

    MongoDB的使用场景: 1.Web应用程序.文档能表示丰富的数据结构,建模相同数据库所需的集合数量通常会比使用完全正规化关系型数据库的数据表数量要少.动态查询和二级索引能让你轻松的实现SQL开发者 ...

  7. SNP问题大集锦

    SNP问题大集锦 [2017-01-19]       最近小编对基因检测很感兴趣,也跟风去测了一下,这一测不要紧,吓得小编几天没睡着觉,这不,检测报告上称小编的减肥能力弱,虽然小编一家都是胖子,唯有 ...

  8. swift 设置string 中汉字中变色等处理代码

    我们在做弹窗 或者显示label string的时候经常会用到字体变色 变大 等特殊处理, swift中提供一个函数 NSMutableAttributedString 使用方法简介 var main ...

  9. dwr 框架 ,实现 ajax 的java 框架

    1. 引入 dwr.jar 包 2. 配置web.xml 文件 ,拦截请求 <servlet> <servlet-name>dwr-invoker</servlet-na ...

  10. part1:10-TFTP与NFS服务器配置

    1.交叉开发 嵌入式系统开发多采用交叉开发模式,所谓嵌入式交叉开发就是指在宿主机上进行程序的编写,然后通过交叉编译生成目标机平台可以运行的二进制代码,最后再下载到目标平台上的特定位置运行.产生嵌入式软 ...