zoukankan      html  css  js  c++  java
  • [bzoj3238] [Ahoi2013] 差异

    Description

    给定一个长度为 (n) 的字符串 (S),令 (Ti) 表示它从第 (i) 个字符开始的后缀。求
    (sumlimits_{1 leq i <j leq n} len(Ti)+len(Tj)-2 imes lcp(Ti,Tj))
    其中,(len(a)) 表示字符串 (a) 的长度,(lcp(a,b)) 表示字符串 (a) 和字符串 (b) 的最长公共前缀。

    Input

    一行,一个字符串 (S)

    Output

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

    Sample Input

    cacao

    Sample Output

    54

    HINT

    对于 (100%) 的数据,保证 (2 leq n leq 500000),且均为小写字母。


    想法

    化简一下要求的那个式子

    [egin{equation*} egin{aligned} &sumlimits_{1 leq i <j leq n} len(Ti)+len(Tj)-2 imes lcp(Ti,Tj) \ =&frac{n(n+1)(n-1)}{2} -2 imes sumlimits_{1 leq i <j leq n} lcp(Ti,Tj) end{aligned} end{equation*} ]

    不妨设 (x= sumlimits_{1 leq i <j leq n} lcp(Ti,Tj)) ,我们要求的就是它
    建出后缀自动机,对于其中每个节点,预处理出它可以到的结束节点的个数 (size) ,这个点对 (x) 的贡献就是 (frac{size(size-1)}{2})


    代码

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    
    using namespace std;
    
    const int N = 500005;
    typedef long long ll;
    
    struct node{
        int len,size;
        node *ch[26],*pa;
    }pool[N*2],*root,*last;
    int cnt;
    void insert(int c){
        node *p=last,*cur=&pool[++cnt];
        cur->len=p->len+1;
        for(;p && !p->ch[c];p=p->pa) p->ch[c]=cur;
        if(!p) cur->pa=root;
        else{
            node *q=p->ch[c],*nq;
            if(q->len==p->len+1) cur->pa=q;
            else{
                nq=&pool[++cnt];
                nq->len=p->len+1;
                for(int i=0;i<26;i++) nq->ch[i]=q->ch[i];
                nq->pa=q->pa;
                q->pa=cur->pa=nq;
                for(;p && p->ch[c]==q;p=p->pa) p->ch[c]=nq;
            }
        }
        last=cur;
    }
    
    char s[N];
    int n;
    
    ll ans,del[N*2];
    int vis[N*2];
    void Get_size(node *p){
        if(vis[p-pool]) {
            ans-=del[p-pool];
            return;
        }
        ll pre=ans;
        for(int i=0;i<26;i++){
            if(!p->ch[i]) continue;
            Get_size(p->ch[i]);
            p->size+=p->ch[i]->size;
        }
        if(p->size>=2) ans-=(ll)p->size*(p->size-1);
        del[p-pool]=pre-ans; vis[p-pool]=1;
    }
    
    int main()
    {
        scanf("%s",s);
        n=strlen(s);
        
        root=&pool[++cnt];
        last=root;
        for(int i=0;i<n;i++) insert(s[i]-'a');
        
        node *tmp=last;
        for(;tmp!=root;tmp=tmp->pa) tmp->size=1;
        ans=(ll)n*(n+1)/2*(n-1);
        Get_size(root);
        
        printf("%lld
    ",ans+(ll)n*(n-1));
        
        return 0;
    }
    
    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    【基础】Oracle 表空间和数据文件
    oracle学习笔记1:windows下oracle数据库安装及.net调用oracle数据库
    redis中文网站
    .NET中的消息队列
    .Net下的进程间的通讯 -- Windows消息队列
    1060 最复杂的数(反素数玄学dfs)
    1282 时钟(最小表示法+hash)
    1191 消灭兔子(贪心+优先队列)
    1366 贫富差距(floyed)
    1503 猪和回文(DP)
  • 原文地址:https://www.cnblogs.com/lindalee/p/10702640.html
Copyright © 2011-2022 走看看