题目大意:

给一个字符串,多次询问k个后缀,求它们两两间LCP长度总和

分析:

转化为后缀树,用虚树求

注意:

后缀树中代表后缀的点都是叶子节点

题目中取模并没有卵用

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
using namespace std;
typedef long long LL;
const int M=1000007;

inline int rd(){
    int x=0;bool f=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=0;
    for(;isdigit(c);c=getchar()) x=x*10+c-48;
    return f?x:-x;
}

int n,m;
char s[M];
int ch[M][26];
int pre[M];
LL len[M],sz[M];
int last=1,tot=1;
int g[M],te,hd[M],td;
struct edge{int y,next;}e[M],dw[M];
int dfn[M],tdfn;
LL ans;
int ln[M<<1],pos[M],T;
int a[M<<1][23];
int que[M*3];
int st[M],cnt;
int id[M];

void addedge(int x,int y){
    e[++te].y=y;
    e[te].next=g[x];
    g[x]=te;
}

void addlink(int x,int y){
    if(x==y) return ;
    dw[++td].y=y;
    dw[td].next=hd[x];
    hd[x]=td;
}

int newnode(int x){
    ++tot;
    len[tot]=x;
    return tot;
}

void Sam(int x){
    int p,q,np,nq;
    p=last;
    last=np=newnode(len[last]+1);
    for(;p&&!ch[p][x];p=pre[p]) ch[p][x]=np;
    if(!p) pre[np]=1;
    else{
        q=ch[p][x];
        if(len[q]==len[p]+1) pre[np]=q;
        else{
            nq=newnode(len[p]+1);
            for(int i=0;i<26;i++) ch[nq][i]=ch[q][i];
            pre[nq]=pre[q];
            pre[q]=pre[np]=nq;
            for(;p&&ch[p][x]==q;p=pre[p]) ch[p][x]=nq;
        }
    }
}

void dfs(int x){
    dfn[x]=++tdfn;
    a[pos[x]=++T][0]=x;
    int p,y;
    for(p=g[x];p;p=e[p].next){
        y=e[p].y;
        dfs(y);
        a[++T][0]=x;
    }
}

int mn(int x,int y){
    return dfn[x]<dfn[y]?x:y;
}

void init(){
    int i,j,l;
    for(i=2;i<=T;i++) ln[i]=ln[i>>1]+1;
    for(i=T;i>0;i--){
        l=ln[T-i+1];
        for(j=1;j<=l;j++)
            a[i][j]=mn(a[i][j-1],a[i+(1<<j-1)][j-1]);
    }
}

int LCA(int x,int y){
    x=pos[x],y=pos[y];
    if(x>y)swap(x,y);
    int l=ln[y-x+1];
    return mn(a[x][l],a[y-(1<<l)+1][l]);
}

bool cmp(int x,int y){return dfn[x]<dfn[y];}

void vbuild(int z){
    int i,x,anc;
    sort(que+1,que+z+1,cmp);
    z=unique(que+1,que+z+1)-(que+1);
    for(i=1;i<z;i++){
        x=LCA(que[i],que[i+1]);
        hd[x]=0; sz[x]=0;
    }
    hd[1]=0; sz[1]=0;
    for(i=1;i<=z;i++){
        x=que[i];
        hd[x]=0; sz[x]=1;
    }
    td=cnt=0;
    st[++cnt]=1;
    for(i=1;i<=z;i++){
        x=que[i];
        anc=LCA(x,st[cnt]);
        if(anc==st[cnt]) st[++cnt]=x;
        else{
            while(cnt>1 && anc==LCA(st[cnt-1],x)){
                addlink(st[cnt-1],st[cnt]);
                cnt--;
            }
            addlink(anc,st[cnt]);
            st[cnt]=anc;
            st[++cnt]=x;
        }
    }
    for(i=1;i<cnt;i++) addlink(st[i],st[i+1]);
}

void dp(int x){
    int p,y;
    for(p=hd[x];p;p=dw[p].next){
        y=dw[p].y;
        dp(y);
        ans+=(sz[x]*sz[y])*len[x];
        sz[x]+=sz[y];
    }
}

int main(){
    int i,z;
    n=rd();m=rd();
    scanf("%s",s+1);
    for(i=n;i>0;i--){
        Sam(s[i]-'a');
        id[i]=last;
    }
    for(i=2;i<=tot;i++) addedge(pre[i],i);
    dfs(1);
    init();
    while(m--){
        z=rd();
        for(i=1;i<=z;i++) que[i]=id[rd()];
        vbuild(z);
        ans=0;
        dp(1);
        printf("%lld\n",ans);
    }
    return 0;
}

bzoj 3879 虚树的更多相关文章

  1. bzoj 2286(虚树+树形dp) 虚树模板

    树链求并又不会写,学了一发虚树,再也不虚啦~ 2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5002  Sol ...

  2. BZOJ 3879: SvT [虚树 后缀树]

    传送门 题意: 多次询问,给出一些后缀,求两两之间$LCP$之和 哈哈哈哈哈哈哈竟然$1A$了,刚才还在想如果写不好这道题下节数学就不上了,看来是上天让我上数学课啊 $Suffix\ Virtual\ ...

  3. BZOJ 3879: SvT 虚树 + 后缀自动机

    Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始 ...

  4. BZOJ.5287.[AHOI HNOI2018]毒瘤(虚树 树形DP)

    BZOJ LOJ 洛谷 设\(f[i][0/1]\)表示到第\(i\)个点,不选/选这个点的方案数.对于一棵树,有:\[f[x][0]=\prod_{v\in son[x]}(f[v][0]+f[v] ...

  5. BZOJ.2286.[SDOI2011]消耗战(虚树 树形DP)

    题目链接 BZOJ 洛谷P2495 树形DP,对于每棵子树要么逐个删除其中要删除的边,要么直接断连向父节点的边. 如果当前点需要删除,那么直接断不需要再管子树. 复杂度O(m*n). 对于两个要删除的 ...

  6. Bzoj 2286 & Luogu P2495 消耗战(LCA+虚树+欧拉序)

    题面 洛谷 Bzoj 题解 很容易想到$O(nk)$的树形$dp$吧,设$f[i]$表示处理完这$i$颗子树的最小花费,同时再设一个$mi[i]$表示$i$到根节点$1$路径上的距离最小值.于是有: ...

  7. BZOJ 3572 [HNOI2014]世界树 (虚树+DP)

    题面:BZOJ传送门 洛谷传送门 题目大意:略 细节贼多的虚树$DP$ 先考虑只有一次询问的情况 一个节点$x$可能被它子树内的一个到x距离最小的特殊点管辖,还可能被管辖fa[x]的特殊点管辖 跑两次 ...

  8. BZOJ 2286 消耗战 (虚树+树形DP)

    给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...

  9. BZOJ 2286 树链剖分+DFS序+虚树+树形DP

    第一次学习虚树,就是把无关的点去掉.S里维护一条链即可. #include <iostream> #include <cstring> #include <cstdio& ...

随机推荐

  1. Long-Polling, Websockets, SSE(Server-Sent Event), WebRTC 之间的区别

    在下面的示例中,客户端指的是浏览器,服务器指的是网站服务器主机. 为了更好的理解这些知识点,你应该简单了解典型的http网站是如何工作的. 普通的http: 客户端从服务器端请求网页 服务器作出相应的 ...

  2. jersey inject

    http://stackoverflow.com/questions/27665744/how-to-inject-an-object-into-jersey-request-context

  3. C#窗口实现最小化到系统托盘

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  4. js中Object.__proto__===Function.prototype

    参考:http://stackoverflow.com/questions/650764/how-does-proto-differ-from-constructor-prototype http:/ ...

  5. ng-init小解

    ng-init可有多个表达式 ng-init= "a= 1;b= 2" 在这里头定义的变量会加入scope作用域 ng-init只能加入不必要的简单逻辑,输入alert() 定义数 ...

  6. Simple But Useful Samples About 'grep' Command(简单实用的grep 命令)

    Do the following: grep -rnw '/path/to/somewhere/' -e "pattern" -r or -R is recursive, -n i ...

  7. layer属性

    键: 值 描述 下表的属性都是默认值,您可在调用时按需重新配置,他们可帮助你实现各式各样的风格.如是调用: $.layer({键: 值, 键: 值, -}); type: 0 层的类型.0:信息框(默 ...

  8. 添加一个Application Framework Service

    如何添加一个Application Framework Service(without native code)? 1.本文参照AlarmManagerService实现一个简单的Applicatio ...

  9. MYSQL启用日志,查看日志,利用mysqlbinlog工具恢复MySQL数据库【转载】

    转自 MYSQL启用日志,查看日志,利用mysqlbinlog工具恢复MySQL数据库 - _安静 - 博客园http://www.cnblogs.com/xionghui/archive/2012/ ...

  10. hibernate事务并发问题(脏读,不可重复读,幻读)

    脏读  dirty read:  读了别的事务没有提交的事务, 可能回滚, 数据可能不对. 不可重复读 non repeatable read: 同一个事务里前后读出来的数据不一样, 被另一个事务影响 ...