• 【bzoj3238】【Ahoi2013】差异



    • 题解:

      • 分成两部分来求,$sum (len(T_{i})+len(T_{j}))$显然是:$frac{(N-1)N(N+1)}{2}$
      • 另外一部分即所有后缀的$lcp$之和的二倍,枚举每一个$sa[i]$;
      • 这样对于每个j从小到大的$sa[j](j<i)$来说和$sa[i]$的$lcp$是连续的且单调递增,这取决于$j+1$到$i$的$height$最小值;
      • 求出$sa$和$ht$后,用一个单调上升的队列维护每一段的答案;
         1 #include<bits/stdc++.h>
         2 #define ll long long
         3 #define rg register
         4 using namespace std;
         5 const int N=500010;
         6 int len,c[N],x[N],y[N],sa[N],top,st[N],rk[N],ht[N];
         7 ll ans,sum[N];
         8 char s[N]; 
         9 void build_sa(int n,int m){
        10     for(rg int i=0;i<n;i++)c[x[i]=s[i]]++;
        11     for(rg int i=1;i<m;i++)c[i]+=c[i-1];
        12     for(rg int i=n-1;~i;i--)sa[--c[x[i]]]=i;
        13     for(rg int k=1;k<n;k<<=1){
        14         int p = 0;
        15         for(rg int i=n-k;i<n;i++)y[p++]=i;
        16         for(rg int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
        17         for(rg int i=0;i<m;i++)c[i]=0;
        18         for(rg int i=0;i<n;i++)c[x[i]]++;
        19         for(rg int i=1;i<m;i++)c[i]+=c[i-1];
        20         for(rg int i=n-1;~i;i--)sa[--c[x[y[i]]]]=y[i];
        21         swap(x,y);
        22         p=0;x[sa[0]]=p++; 
        23         for(rg int i=1;i<n;i++){
        24             x[sa[i]] = y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k] ? p-1 : p++;
        25         }
        26         if(p==n)break;
        27         m = p + 1;
        28     }
        29 }
        30 void build_ht(int n){
        31     for(rg int i=0;i<n;i++)rk[sa[i]]=i;
        32     for(rg int i=0,j,k=0;i<n;i++){
        33         if(k)k--;
        34         j=sa[rk[i]-1];
        35         while(s[i+k]==s[j+k])k++;
        36         ht[rk[i]] = k;
        37     }
        38 }
        39 int main(){
        40     freopen("bzoj3238.in","r",stdin);
        41     freopen("bzoj3238.out","w",stdout);
        42     scanf("%s",s);
        43     len = strlen(s);
        44     ans = (ll)len*(len+1)/2*(len-1);
        45     s[len]='$';
        46     build_sa(len+1,128);
        47     build_ht(len+1);
        48     for(rg int i=0;i<=len;i++){
        49         while(top&&ht[st[top]]>=ht[i])top--;
        50         st[++top]=i;
        51         sum[top] = sum[top-1] + 2ll * (st[top] - st[top-1]) * ht[i];
        52         ans -= sum[top];
        53     }
        54     printf("%lld
        ",ans);
        55     return 0;
        56 }
        bzoj3238(SA)
      • upd:20190106  学会了$SAM$的解法
      • 还是考虑$sum lcp(i,j)$  (下文的$len$指节点$right$集合的最大长度,$sum$为节点的$parent$子树里的后缀个数)
      • 后缀树和$parent$树的联系:
        • 在$parent$树上前缀的最长后缀是在他们对应节点的$lca$的$len$;
        • 那么我们将串反过来建$SAM$的话$parent$树可以回答后缀间的$lcp$了;
      • 节点$u$作为$lca$的次数$*2$是:$sum_{u}  (sum_{u} - 1)  -  sum_{v} sum_{v} (sum_{v}-1)$ 
      • 整理一下每个点贡献为:$sum_{u}(sum_{u}-1) * (len[u] - len[pa[u]]) $
      • 另一种的理解:
      • $sum lcp(i,j)  = sum_{子串x}  * sum_{i} sum_{j} [x是后缀i,j的公共前缀] $

      • 每个节点的不同子串个数为$len[u]-len[pa[u]]$立即推出式子;
      • 其实仔细想想对这个题不翻转答案也是一样的QAQ;
      •  1 #include<bits/stdc++.h>
         2 #define ll long long
         3 using namespace std;
         4 const int N=1000010;
         5 int n,lst,ch[N][26],cnt,len[N],pa[N],w[N],a[N],v[N];
         6 char s[N];
         7 inline void ins(int c){
         8     int p=lst,np; 
         9     len[lst=np=++cnt]=len[p]+1;
        10     v[np]=1;
        11     while(p&&!ch[p][c])ch[p][c]=np,p=pa[p];
        12     if(!p)pa[np]=1;
        13     else {
        14         int q=ch[p][c];
        15         if(len[q]==len[p]+1)pa[np]=q;
        16         else{
        17             int nq=++cnt;
        18             len[nq]=len[p]+1;
        19             memcpy(ch[nq],ch[q],sizeof(ch[q]));
        20             pa[nq]=pa[q];pa[q]=pa[np]=nq;
        21             while(p&&ch[p][c]==q)ch[p][c]=nq,p=pa[p];
        22         }
        23     }
        24 }
        25 int main(){
        26     #ifndef ONLINE_JUDGE 
        27     freopen("bzoj3238.in","r",stdin);
        28     freopen("bzoj3238.out","w",stdout);
        29     #endif
        30     scanf("%s",s);
        31     lst=cnt=1;
        32     n=strlen(s);
        33     for(int i=0;i<n;i++)ins(s[i]-'a');
        34     for(int i=1;i<=cnt;i++)w[len[i]]++;
        35     for(int i=1;i<=n;i++)w[i]+=w[i-1];
        36     for(int i=cnt;i;i--)a[w[len[i]]--]=i;
        37     ll ans = (ll)n*(n-1)*(n+1)/2;
        38     for(int i=cnt;i>1;i--){
        39         int u=a[i];
        40         v[pa[u]]+=v[u];
        41         ans -= (ll)(len[u]-len[pa[u]])*v[u]*(v[u]-1);
        42     }
        43     cout << ans << endl;
        44     return 0;
        45 }
        bzoj3238(SAM)
  • 相关阅读:
    一个很好的菜单源码
    在盗版xp下安装ie7正式版 
    [导入]买新手机了
    [导入]手机解锁全集
    12种找工作方式的成功率
    Kerberos的原理 3
    Kerberos的原理 4
    Kerberos的原理 1
    jQuery 原理的模拟代码 6 代码下载
    Hashtable 中的键值修改问题
  • 原文地址:https://www.cnblogs.com/Paul-Guderian/p/10222617.html
Copyright © 2020-2023  润新知