zoukankan      html  css  js  c++  java
  • BZOJ3238:[AHOI2013]差异(SAM)

    Description

    Input

    一行,一个字符串S

    Output

    一行,一个整数,表示所求值

    Sample Input

    cacao

    Sample Output

    54

    HINT

    2<=N<=500000,S由小写英文字母组成

    Solution

    后缀自动机的fa指针反向以后可以形成一个树结构,称作Parent树
    一个节点的father是他的最长后缀,那么很显然任意两个子串的最长公共后缀位于它们Parent树对应节点的lca处
    为了利用这个性质,可以把串反过来建立SAM,问题转化成对这个串的所有前缀求最长公共后缀
    要注意只有np节点才能代表前缀
    一对对枚举前缀求lcs显然是不可能的,可以考虑对于每个子串,它是多少对前缀的最长公共后缀
    也就是对于每个节点,求它是多少对前缀节点的LCA
    然后DFS一下就好了

    Code

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #define N (1000000+1000)
     5 using namespace std;
     6 
     7 struct Edge{int to,next;}edge[N<<1];
     8 long long ans;
     9 int head[N],num_edge;
    10 char s[N];
    11 
    12 void add(int u,int v)
    13 {
    14     edge[++num_edge].to=v;
    15     edge[num_edge].next=head[u];
    16     head[u]=num_edge;
    17 }
    18 
    19 struct SAM
    20 {
    21     int fa[N],son[N][28],right[N],step[N],od[N],wt[N],size[N];
    22     int p,q,np,nq,last,cnt;
    23     SAM(){last=++cnt;}
    24     
    25     void Insert(int x)
    26     {
    27         p=last; last=np=++cnt; step[np]=step[p]+1; size[np]=1;
    28         while (p && !son[p][x]) son[p][x]=np,p=fa[p];
    29         if (!p) fa[np]=1;
    30         else
    31         {
    32             q=son[p][x];
    33             if (step[p]+1==step[q]) fa[np]=q;
    34             else
    35             {
    36                 nq=++cnt; step[nq]=step[p]+1;
    37                 memcpy(son[nq],son[q],sizeof(son[q]));
    38                 fa[nq]=fa[q]; fa[q]=fa[np]=nq;
    39                 while (son[p][x]==q) son[p][x]=nq,p=fa[p];
    40             }
    41         }
    42     }
    43      void Dfs(int x,int fa)
    44      {
    45          long long sum=0,is_one=(size[x]==1);
    46          for (int i=head[x]; i; i=edge[i].next)
    47              if (edge[i].to!=fa)
    48              {
    49                  Dfs(edge[i].to,x);
    50                  size[x]+=size[edge[i].to];
    51                  ans-=2*sum*size[edge[i].to]*step[x];
    52                  sum+=size[edge[i].to];
    53              }
    54          if (is_one) ans-=2ll*(size[x]-1)*step[x];
    55      }
    56 }SAM;
    57 
    58 int main()
    59 {
    60     scanf("%s",s);
    61     long long len=strlen(s);
    62     ans=(len-1)*len/2*(len+1);
    63     for (int i=len-1; i>=0; --i)
    64         SAM.Insert(s[i]-'a');
    65     for (int i=2; i<=SAM.cnt; ++i)
    66         add(i,SAM.fa[i]),add(SAM.fa[i],i);
    67     SAM.Dfs(1,-1);
    68     printf("%lld",ans);
    69 }
  • 相关阅读:
    思科交换机命令
    Cisco2960 交换机密码破解方法
    洛谷 P2147 [SDOI2008]洞穴勘测
    BZOJ 4025: 二分图
    算法笔记--可撤销并查集 && 可持久化并查集
    P5043 【模板】树同构([BJOI2015]树的同构)
    算法笔记--BSGS && exBSGS 模板
    算法笔记--线性基求交模板
    AcWing 246. 区间最大公约数
    2018年长沙理工大学第十三届程序设计竞赛 I 连续区间的最大公约数
  • 原文地址:https://www.cnblogs.com/refun/p/9364716.html
Copyright © 2011-2022 走看看