题目描述

记 $lcp(i,j)$ 表示 $i$ 表示 $i$ 这个后缀和 $j$ 这个后缀的最长公共后缀长度
给定一个字符串,每次询问的时候给出两个正整数集合 $A$ 和 $B$,求
$\sum_{i\in A,j\in B}lcp(i,j)$ 的值.
 
题解: 对反串建立后缀自动机.
这样,任意两个后缀树节点所代表的字符串的 $LCP$ 值就是两点最近公共祖先在自动机中的 $len$ 值.
问题转化为 $\sum_{i\in A,j\in B}len[LCA(id[i],id[j])]$
其中 $id[i]$ 表示 $S[1...i]$ 所表示的字符串在自动机中的节点.
询问次数很多,不过 $\sum A+B$ 不会很大.
用虚树统计 $\sum_{i\in A,j\in B}len[LCA(id[i],id[j])]$ 即可.  
#include <bits/stdc++.h>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 400004
#define ll long long
using namespace std;
int edges,n,Q;
int hd[maxn],to[maxn],nex[maxn],tr[maxn];
char str[maxn];
void addedge(int u,int v,int c)
{
nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;
}
namespace SAM
{
int last, tot;
int len[maxn],f[maxn],trans[maxn][27];
void init() { last=tot=1; }
int extend(int c)
{
int np=++tot,p=last;
len[np]=len[p]+1,last=np;
while(p&&!trans[p][c]) trans[p][c]=np, p=f[p];
if(!p) f[np]=1;
else
{
int q=trans[p][c];
if(len[q]==len[p]+1) f[np]=q;
else
{
int nq=++tot;
len[nq]=len[p]+1;
memcpy(trans[nq],trans[q],sizeof(trans[q]));
f[nq]=f[q],f[q]=f[np]=nq;
while(p&&trans[p][c]==q) trans[p][c]=nq,p=f[p];
}
}
return np;
}
void build()
{
for(int i=2;i<=tot;++i) addedge(f[i],i,len[f[i]]-len[i]);
}
}
int tim;
int dfn[maxn],Top[maxn],hson[maxn],siz[maxn],fa[maxn],dis[maxn];
void dfs1(int u,int ff)
{
fa[u]=ff,siz[u]=1,dfn[u]=++tim,dis[u]=dis[ff]+1;
for(int i=hd[u];i;i=nex[i])
{
int v=to[i];
if(v==ff) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[hson[u]]) hson[u]=v;
}
}
void dfs2(int u,int tp)
{
Top[u]=tp;
if(hson[u]) dfs2(hson[u],tp);
for(int i=hd[u];i;i=nex[i])
{
int v=to[i];
if(v==fa[u]||v==hson[u]) continue;
dfs2(v,v);
}
}
int LCA(int x,int y)
{
while(Top[x]!=Top[y])
{
dis[Top[x]]>dis[Top[y]]?x=fa[Top[x]]:y=fa[Top[y]];
}
return dis[x]<dis[y]?x:y;
}
vector<int>G[maxn];
ll ans=0;
int t=0,tot=0;
int arr[maxn],brr[maxn],A[maxn<<1],mk[maxn],mka[maxn],mkb[maxn],sizea[maxn],sizeb[maxn],S[maxn];
bool cmp(int i,int j)
{
return dfn[i]<dfn[j];
}
void addvir(int x,int y)
{
G[x].push_back(y);
}
void insert(int x)
{
if(t<=1) { S[++t]=x; return; }
int lca=LCA(S[t],x);
if(lca==S[t]) { S[++t]=x; return; }
while(t>1&&dis[S[t-1]]>=dis[lca]) addvir(S[t-1],S[t]),--t;
if(S[t]!=lca) addvir(lca,S[t]),S[t]=lca;
S[++t]=x;
}
void DP(int x)
{
for(int i=0;i<G[x].size();++i)
DP(G[x][i]), sizea[x]+=sizea[G[x][i]], sizeb[x]+=sizeb[G[x][i]];
for(int i=0;i<G[x].size();++i)
{
ans+=1ll*(sizea[x]-sizea[G[x][i]])*sizeb[G[x][i]]*SAM::len[x];
}
ans+=(1ll*mka[x]*mkb[x]+1ll*mka[x]*sizeb[x]+1ll*mkb[x]*sizea[x])*SAM::len[x];
sizea[x]+=mka[x],sizeb[x]+=mkb[x];
}
void erase(int x)
{
mka[x]=mkb[x]=sizea[x]=sizeb[x]=0;
for(int i=0;i<G[x].size();++i) erase(G[x][i]);
G[x].clear();
}
void work()
{
tot=0;
int k1,k2;
scanf("%d%d",&k1,&k2);
for(int i=1;i<=k1;++i) scanf("%d",&arr[i]),A[++tot]=tr[arr[i]],mka[tr[arr[i]]]=1;
for(int i=1;i<=k2;++i) scanf("%d",&brr[i]),A[++tot]=tr[brr[i]],mkb[tr[brr[i]]]=1;
sort(A+1,A+1+tot,cmp);
tot=unique(&A[1],&A[tot+1])-A-1;
t=ans=0;
if(A[1]!=1) S[t=1]=1;
for(int i=1;i<=tot;++i) insert(A[i]);
while(t>1) addvir(S[t-1],S[t]),--t;
DP(1);
printf("%I64d\n",ans);
erase(1);
}
int main()
{
// setIO("input");
scanf("%d%d%s",&n,&Q,str+1);
SAM::init();
for(int i=n;i>=1;--i) tr[i]=SAM::extend(str[i]-'a');
SAM::build();
dfs1(1,0),dfs2(1,1);
for(int i=1;i<=Q;++i) work();
return 0;
}

  

CF1073G Yet Another LCP Problem 后缀自动机 + 虚树 + 树形DP的更多相关文章

  1. bzoj3879 SvT(后缀自动机+虚树)

    bzoj3879 SvT(后缀自动机+虚树) bzoj 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置 ...

  2. 【BZOJ-3572】世界树 虚树 + 树形DP

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status ...

  3. 【BZOJ-2286】消耗战 虚树 + 树形DP

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status] ...

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

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

  5. BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca

    BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...

  6. BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP

    题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...

  7. [WC2018]通道——边分治+虚树+树形DP

    题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...

  8. 2018.09.25 bzoj3572: [Hnoi2014]世界树(虚树+树形dp)

    传送门 虚树入门题? 好难啊. 在学习别人的写法之后终于过了. 这道题dp方程很好想. 主要是不好写. 简要说说思路吧. 显然最优值只能够从子树和父亲转移过来. 于是我们先dfs一遍用儿子更新父亲,然 ...

  9. 洛谷 P4248 / loj 2377 [AHOI2013] 差异 题解【后缀自动机】【树形DP】

    可能是一个 SAM 常用技巧?感觉 SAM 的基础题好多啊.. 题目描述 给定一个长度为 \(n\) 的字符串 \(S\) ,令 \(T_i\) 表示它从第 \(i\) 个字符开始的后缀,求: \[ ...

随机推荐

  1. linux文本图形界面转换

    vim /etc/inittab 3为默认进入文本界面, 5为默认进入图形界面 文本界面下输入init5或者startx切换图形化界面  图形化界面下输入init3切换文本界面

  2. day57——ajax之初体验

    转行学开发,代码100天——2018-05-12 今天是一个特别的日子——首先是母亲节,在此也祝福亲爱的妈妈健康长寿.其次今天是汶川大地震10周年,10年过去了,经历过苦难的人更加坚毅勇敢地面向未来! ...

  3. 在静态页面中使用 Vue.js

    在静态页面中使用 Vue.js 不使用Node.js, NPM, Webpack 等, 在静态页中使用Vue.js. 包括路由, 单文件组件. 1. 创建index.html index.html做为 ...

  4. delphi 静态3维数组。 严重占用堆栈 切记。 应该用动态数组, 非要用静态数组的话, 要在编译器里 把 堆栈 调大

    delphi 代码正确, 但是运行就崩溃. 原因为 定义了  一些   静态3维数组. 应该扩大 软件的 堆栈 设置.    然后正常解决问题 静态3维数组.   严重占用堆栈   切记. 应该用动态 ...

  5. 关于staticmethod() 函数

    说实话,我就不知这个是干什么的. 菜鸟教程写的无需实例化, 自己可以调用自己. 在同一个类面我使用到了 因为一个类了, 我可能会方法间互相调用. 类中间使用.不加这个,就会报错.无法识别这个 orig ...

  6. jQ全选或取消全选

    function checkAll(chkobj) {        if ($(chkobj).children("span").text() == "全选" ...

  7. python+selenium下拉列表option对象操作方法一

    参考官方文档:https://selenium.dev/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.select ...

  8. Hibernate的HQL多表查询

    HQL的内连接查询 对于HQL内链接查询,查询的是两张表的数据,这两张表的数据首先是保存在数组之中,然后在将每一个数组保存在List集合之中进行返回 代码片段: @Test // 内连接 public ...

  9. [19/05/17-星期五] HTML_body标签(内嵌标签)和框架标签

    一.内嵌标签 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <!- ...

  10. CentOS7 通过YUM安装MySQL5.7 linux

    CentOS7 通过YUM安装MySQL5.7 1.进入到要存放安装包的位置 cd /home/lnmp 2.查看系统中是否已安装 MySQL 服务,以下提供两种方式: rpm -qa | grep  ...