• 题意:给一个长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. Hibernate快速上手

    一. Hibernate介绍 1. Hibernate简介 Hibernate是一个开放源码的对象-关系映射(ORM)框架,他对JDBC进行了轻量级封装,开发人员可以使用面向对象的编程思想来进行持久层 ...

  2. python 反序列化

    Python-反序列化函数使用 pickle.dump(obj, file) : 将对象序列化后保存到文件 pickle.load(file) : 读取文件, 将文件中的序列化内容反序列化为对象 pi ...

  3. JavaEE期末复习知识点总结

    JavaEE期末复习知识点总结 Java企业应用开发环境 Maven的基础概念 Maven是一个项目管理工具,可以对 Java 项目进行构建.依赖管理 Maven仓库 Maven 仓库是项目中依赖的第 ...

  4. Qt QPropertyAnimation+QTimer实现自制悬浮窗

    目录 Qt下的悬浮窗 QPropertyAnimation QTimer 事件过滤 图标变换 自适应窗口大小 使用方法 Qt下的悬浮窗 最近项目需要一个类似于360悬浮球类似的悬浮窗,当鼠标放入停留一 ...

  5. Zalando Postgres Operator 快速上手

    本指南旨在让您快速了解在本地 Kubernetes 环境中使用 Postgres Operator. 前提条件 由于 Postgres Operator 是为 Kubernetes (K8s) 框架设 ...

  6. 8.Jenkins进阶之流水线pipeline基础使用实践(1)

    ​目录一览: 0x01 基础实践 (1) Maven 构建之 Pipeline Script (2) Maven 构建之 Pipeline Script from SCM (3) Jenkins pi ...

  7. The 18th Zhejiang Provincial Collegiate Programming Contest

    The 18th Zhejiang Provincial Collegiate Programming Contest GYM链接 https://codeforces.com/gym/103055 ...

  8. el-menu菜单 -- unique-opened 子菜单唯一性失效

    总结: 点击的是  el-sub-menu . 所以  el-sub-menu 的唯一性是必须的.否则 unique-opened 属性不生效

  9. DOS攻击(一)

    DOS攻击(一) 介绍 DoS是Denial of Service的简称,即拒绝服务,造成DoS的攻击行为被称为DoS攻击,其目的是使计算机或网络无法提供正常的服务.最常见的DoS攻击有计算机网络带宽 ...

  10. java 中为什么重写 equals 后需要重写 hashCode

    本文为博主原创,未经允许不得转载: 1. equals 和 hashCode 方法之间的关系 这两个方法都是 Object 的方法,意味着 若一个对象在没有重写 这两个方法时,都会默认采用 Objec ...