• 题意:给一个长n的字符串S,q组询问,每组给两个集合A,B。求集合A中的点和集合B中的点所有组合情况的lcp的和。
  • 思路:

    好像比较常规,可是代码能力差还是调了1.5h。主要还是虚树板子不熟(加入的时候点要去重)

    SAM+虚树+虚树上dp

    两个后缀的lca相当于后缀树上两个对应节点的LCA的len。

    dp就统计每个点为lca的方案数*len[lca]
  • code:
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5;
char s[N];
typedef long long ll;
int lg[N],a[N],b[N],d[N],pos[N],nxt[N],to[N],head[N],ecnt;
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
struct SAM {
int f[N][21],dep[N],ch[N][27],t[N],len[N],lst,num,par[N],sz[N],Head[N],To[N],Nxt[N],Ecnt,In[N],Out[N],Time;
SAM() {lst=num=1;}
void Add_edge(int u,int v) {Nxt[++Ecnt]=Head[u];To[Ecnt]=v;Head[u]=Ecnt;}
int Insert(int x) {
int p=lst,np=++num;lst=np;len[np]=len[p]+1;sz[np]=1;
for(;!ch[p][x];p=par[p]) ch[p][x]=np;
if(!p) {par[np]=1;return np;}
int q=ch[p][x];
if(len[q]==len[p]+1) {par[np]=q;return np;}
int nq=++num;par[nq]=par[q];len[nq]=len[p]+1;
for(int j=0;j<26;j++)ch[nq][j]=ch[q][j];
par[q]=par[np]=nq;
for(;ch[p][x]==q;p=par[p])ch[p][x]=nq;
return np;
}
void dfs(int u) {
In[u]=++Time;
for(int i=Head[u];i;i=Nxt[i]) {
int v=To[i];
dep[v]=dep[u]+1;f[v][0]=u;
for(int j=1;j<=lg[dep[v]];j++) f[v][j]=f[f[v][j-1]][j-1];
dfs(v);
}
Out[u]=Time;
}
void bd_Fail() {
for(int i=1;i<=num;i++) if(par[i])Add_edge(par[i],i);
dfs(1);
}
int Lca(int u,int v) {
if(dep[u]<dep[v])swap(u,v);
int k=dep[u]-dep[v];for(int i=0;i<=lg[k];i++)if((1<<i)&k)u=f[u][i];
if(u==v) return u;
for(int i=lg[dep[u]];i>=0;i--) if(f[u][i]!=f[v][i])u=f[u][i],v=f[v][i];
return f[u][0];
}
}A;
bool cmp(int u,int v) {return A.In[u]<A.In[v];}
int sa[N],sb[N],st[N],tp;
ll ans=0;
void DP(int u) {
// printf("#%d %d\n",u,A.len[u]);
ans+=1ll*A.len[u]*(sa[u]*sb[u]);
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
DP(v);
ans+=A.len[u]*(1ll*sa[v]*sb[u]+1ll*sa[u]*sb[v]);
sa[u]+=sa[v],sb[u]+=sb[v];
}
}
bool mark[N];
int main() {
int n,q;scanf("%d%d",&n,&q);
scanf("%s",s);
for(int i=n-1;i>=0;i--)pos[i+1]=A.Insert(s[i]-'a');
lg[1]=0;for(int i=2;i<=(n<<1);i++)lg[i]=lg[i>>1]+1;
A.bd_Fail();
while(q--) {
int ca,cb,up=0;
scanf("%d%d",&ca,&cb);
for(int i=1;i<=ca;i++) {scanf("%d",&a[i]);d[++up]=a[i]=pos[a[i]];mark[a[i]]=1;}
for(int i=1;i<=cb;i++) {scanf("%d",&b[i]);b[i]=pos[b[i]];if(!mark[b[i]])d[++up]=b[i],mark[b[i]]=1;}
sort(d+1,d+1+up,cmp);
for(int i=1,sz=up;i<sz;i++) {
d[++up]=A.Lca(d[i],d[i+1]);
if(mark[d[up]])up--;else mark[d[up]]=1;
}
sort(d+1,d+1+up,cmp);
// puts("");for(int i=1;i<=up;i++) printf("%d ",d[i]);puts("");
st[++tp]=d[1];
for(int i=2;i<=up;i++) {
int u=A.In[d[i]];
while(tp&&(u<A.In[st[tp]]||u>A.Out[st[tp]]))tp--;
if(tp)add_edge(st[tp],d[i]);
// printf("!%d %d\n",st[tp],d[i]);
st[++tp]=d[i];
}
for(int i=1;i<=ca;i++)sa[a[i]]=1;
for(int i=1;i<=cb;i++)sb[b[i]]=1;
DP(d[1]);
printf("%lld\n",ans);
tp=ecnt=ans=0;for(int i=1;i<=up;i++)mark[d[i]]=sa[d[i]]=sb[d[i]]=head[d[i]]=0;
}
return 0;
}

[CF1073G]LCP问题的更多相关文章

  1. CF1073G Yet Another LCP Problem

    题目传送门. 题意简述:给出 \(s\),多次询问给出长度分别为 \(k,l\) 的序列 \(a,b\),求 \(\sum_{i=1}^k\sum_{j=1}^l\mathrm{LCP}(s[a_i: ...

  2. cf1073G Yet Another LCP Problem (SA+权值线段树)

    反正先求一遍sa 然后这个问题可以稍微转化一下 默认比较A.B数组中元素的大小都是比较它们rank的大小,毕竟两个位置的LCP就是它们rank的rmq 然后每次只要求B[j]>=A[i]的LCP ...

  3. CF1073G Yet Another LCP Problem 后缀自动机 + 虚树 + 树形DP

    题目描述 记 $lcp(i,j)$ 表示 $i$ 表示 $i$ 这个后缀和 $j$ 这个后缀的最长公共后缀长度给定一个字符串,每次询问的时候给出两个正整数集合 $A$ 和 $B$,求$\sum_{i\ ...

  4. 【BZOJ】1014: [JSOI2008]火星人prefix(splay+hash+二分+lcp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1014 题意:支持插入一个字符.修改一个字符,查询lcp.(总长度<=100000, 操作< ...

  5. poj 2774 Long Long Message 后缀数组LCP理解

    题目链接 题意:给两个长度不超过1e5的字符串,问两个字符串的连续公共子串最大长度为多少? 思路:两个字符串连接之后直接后缀数组+LCP,在height中找出max同时满足一左一右即可: #inclu ...

  6. hdu 3518 Boring counting 后缀数组LCP

    题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output ...

  7. BZOJ2105: 增强型LCP

    2105: 增强型LCP Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 366  Solved: 86[Submit][Status] Descrip ...

  8. LCP Array(思维)

    LCP Array Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Tota ...

  9. hdu 4691 最长的共同前缀 后缀数组 +lcp+rmq

    http://acm.hdu.edu.cn/showproblem.php? pid=4691 去年夏天,更多的学校的种族称号.当时,没有后缀数组 今天将是,事实上,自己的后缀阵列组合rmq或到,但是 ...

随机推荐

  1. 有关placeholder在ie9中的一点折腾

    有关placeholder在ie9中的一点折腾. placeholder属性定义: placeholder 属性规定可描述输入字段预期值的简短的提示信息(比如:一个样本值或者预期格式的短描述). 问题 ...

  2. 关于mui中一个页面有有多个页签进行切换的下拉刷新加搜索问题

    此图是最近做的项目中的一页,用的是mui结合vue,用了mui后,觉得是真心难用啊,先不说其他的,就光这个下拉刷新就让人奔溃了,问题层出不穷,不过最后经过努力还是摆平了哈. 1.每次切换到新的标签,都 ...

  3. java中hashCode和equals什么关系,hashCode到底怎么用的

    Object类的hashCode的用法:(新手一定要忽略本节,否则会很惨) 马 克-to-win:hashCode方法主要是Sun编写的一些数据结构比如Hashtable的hash算法中用到.因为ha ...

  4. JS实现列表移动(通过DOM操作select标签)

    JS小例题 学习内容: 需求 总结: 学习内容: 需求 用 JavaScript 实现 select 标签的移动 实现代码 <!DOCTYPE html PUBLIC "-//W3C/ ...

  5. Struts bean:define标签用法

    bean:define:有三个用途 一是定义新字符串常量: <bean:define id="foo" value="This is a new String&qu ...

  6. (动态模型类,我的独创)Django的原生ORM框架如何支持MongoDB,同时应对客户使用时随时变动字段

    1.背景知识 需要开发一个系统,处理大量EXCEL表格信息,各种类别.表格标题多变,因此使用不需要预先设计数据表结构的MongoDB,即NoSQL.一是字段不固定,二是同名字段可以存储不同的字段类型. ...

  7. SpringMVC的数据响应方式-页面跳转

    1.返回字符串形式 直接返回字符串:此种方式会返回字符串与视图解析器的前后缀拼接后跳转 有关视图解析器的拼接请访问此地址 注意:WEB-INF下的资源一般不能访问,因为转发是服务器的操作所以可以访问到 ...

  8. python解释器安装与使用

    Python解释器安装与使用 首先了解下python是由'龟叔' 也就是右边这位和蔼的大叔叔 全名'Guido van Rossum'在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言. ...

  9. Protoc安装

    系统:linux 记住,千万别混乱版本,一般protoc可执行文件在/usr/local/bin/或/usr/bin/下 个人这里使用3.13版本示例,下面两部分命令可以写入shell脚本,记住执行要 ...

  10. Codeforces Round #716 (Div. 2), problem: (B) AND 0, Sum Big位运算思维

    & -- 位运算之一,有0则0 原题链接 Problem - 1514B - Codeforces 题目 Example input 2 2 2 100000 20 output 4 2267 ...