蒟蒻表示不会sam凉凉了,所以只能提高SA技巧?

题意:有一个串\(A\),每次选择一个\(A\)的子串\(A'\),以及串\(B\),问\(B\)的所有本质不同子串中不在\(A'\)中的串的数量。

(定义\(A_i\)表示以字符\(A_i\)开头的后缀,\(B_i\)同理)

\(B\)的本质不同字串显然是\(|B|*(|B|+1)/2\)了,然后要减去本质不同的在\(A'\)中的串

首先把所有串拼一起,把SA建出来,辣么就可以在SA中找到\(A'\)所对应的所有后缀,对于\(B\)对应的每个后缀\(B_i\),计算\(\max_{j=l}^r LCP(A_j,B_i)\),就是在\(A'\)中的前缀数量,加起来就是这个东西了,由于还要判重,所以计入的其实是\(\max(0,\max_{j=l}^r LCP(A_j,B_i)-LCP(B_i,next(B_{i})))\),其中\(next(B_i)\)意思是在\(B\)串的SA上\(B_i\)的后继

\(LCP(B_i,next(B_{i}))\)随便算是吧,现在要算的是\(\max_{j=l}^rLCP(A_j,B_i)\)

要算LCP的max值,显然只要在SA上求出前驱和后继计算就行了,那么要算区间前驱后继,就是二逼平衡树了

#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
#define Log(x) (31-__builtin_clz(x))
il ll gi(){
ll x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
char S[1600037];
int t[100010],lent[100010];
int n,N;
int ql[100010],qr[100010];
namespace SA{
int x[1600037],y[1600037],_[1600037],SA[1600037],rk[1600037],ht[1600037],t[1600037];
int st[21][1600037];
il int LCP(int x,int y){
if(!x||!y)return 0;
if(x==y)return 1e9;
int l=Log(y-x);
return std::min(st[l][x],st[l][y-(1<<l)]);
}
il int gety(int x){return x<=N?y[x]:-1;}
il vd getSA(){
int set=128;
for(int i=1;i<=N;++i)++t[x[i]=S[i]];
for(int i=1;i<=set;++i)t[i]+=t[i-1];
for(int i=N;i;--i)SA[t[x[i]]--]=i;
for(int k=1;k<=N;k<<=1){
int p=0;
for(int i=N-k+1;i<=N;++i)y[++p]=i;
for(int i=1;i<=N;++i)if(SA[i]>k)y[++p]=SA[i]-k;
for(int i=0;i<=set;++i)t[i]=0;
for(int i=1;i<=N;++i)++t[x[y[i]]];
for(int i=1;i<=set;++i)t[i]+=t[i-1];
for(int i=N;i;--i)SA[t[x[y[i]]]--]=y[i];
memcpy(_,x,sizeof _);
memcpy(x,y,sizeof _);
memcpy(y,_,sizeof _);
x[SA[1]]=p=1;
for(int i=2;i<=N;++i){
if(gety(SA[i])!=gety(SA[i-1])||gety(SA[i]+k)!=gety(SA[i-1]+k))++p;
x[SA[i]]=p;
}
if(p>=N)break;set=p;
}
for(int i=1;i<=N;++i)rk[SA[i]]=i;
for(int i=1,j,k=0;i<=N;++i){
if(rk[i]==N)continue;
if(k)--k;
j=SA[rk[i]+1];
while(S[i+k]==S[j+k])++k;
ht[rk[i]]=k;
}
for(int i=1;i<=N;++i)st[0][i]=ht[i];
for(int i=1;i<=Log(N);++i)
for(int j=1;j+(1<<i)-1<=N;++j)
st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<i-1)]);
//for(int i=1;i<=N;++i)printf("%d %s\n",ht[i],S+SA[i]);
}
}
#define mid ((l+r)>>1)
int rt[500010],ls[20000010],rs[20000010],sum[20000010],cnt;
il vd build(int&x,int l,int r){
x=++cnt;if(l==r)return;
build(ls[x],l,mid),build(rs[x],mid+1,r);
}
il vd update(int&x,int l,int r,const int&p){
++cnt;ls[cnt]=ls[x],rs[cnt]=rs[x],sum[cnt]=sum[x];x=cnt;
++sum[x];if(l==r)return;
if(p<=mid)update(ls[x],l,mid,p);
else update(rs[x],mid+1,r,p);
}
int pp;
il int query_nxt(int x,int y,int l,int r){
if(!(sum[y]-sum[x]))return 0;
if(l==r)return l;
if(mid<pp)return query_nxt(rs[x],rs[y],mid+1,r);
if(pp<l){
if(sum[ls[y]]-sum[ls[x]])return query_nxt(ls[x],ls[y],l,mid);
else return query_nxt(rs[x],rs[y],mid+1,r);
}else{
int t=query_nxt(ls[x],ls[y],l,mid);
return t?t:query_nxt(rs[x],rs[y],mid+1,r);
}
}
il int query_pre(int x,int y,int l,int r){
if(!(sum[y]-sum[x]))return 0;
if(l==r)return l;
if(pp<=mid)return query_pre(ls[x],ls[y],l,mid);
if(r<pp){
if(sum[rs[y]]-sum[rs[x]])return query_pre(rs[x],rs[y],mid+1,r);
else return query_pre(ls[x],ls[y],l,mid);
}else{
int t=query_pre(rs[x],rs[y],mid+1,r);
return t?t:query_pre(ls[x],ls[y],l,mid);
}
}
#undef mid
il bool check(int l,int r,int p,int k){
if(l>r)return 0;
pp=p;
if(SA::LCP(query_pre(rt[l-1],rt[r],1,N),p)>=k)return 1;
if(SA::LCP(p,query_nxt(rt[l-1],rt[r],1,N))>=k)return 1;
return 0;
}
int lcp[1600037];
int main(){
#ifdef XZZSB
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
scanf("%s",S+1);n=strlen(S+1);N=n+1;
int Q=gi();t[1]=n+1;S[n+1]='~';
int sumt=0;
for(int i=1;i<=Q;++i){
scanf("%s",S+t[i]+1);ql[i]=gi(),qr[i]=gi();
lent[i]=strlen(S+t[i]+1);t[i+1]=t[i]+lent[i]+1;N+=lent[i]+1;
sumt+=lent[i];
S[t[i]+lent[i]+1]='|';
}
SA::getSA();
build(rt[0],1,N);
for(int i=1;i<=n;++i)rt[i]=rt[i-1],update(rt[i],1,N,SA::rk[i]);
int l,r;
for(int o=1;o<=Q;++o){
l=ql[o],r=qr[o];
std::vector<int>sufs;
for(int i=t[o]+1;i<=t[o]+lent[o];++i)sufs.push_back(SA::rk[i]);
std::sort(sufs.begin(),sufs.end());
ll res=1ll*lent[o]*(lent[o]+1)/2;
for(int i=1;i<sufs.size();++i)res-=(lcp[SA::SA[sufs[i-1]]]=SA::LCP(sufs[i-1],sufs[i]));
for(int i=t[o]+1,j=0;i<=t[o]+lent[o];++i){
if(j)--j;
while(check(l,r-j,SA::rk[i],j+1))++j;
res-=std::max(0,j-lcp[i]);
}
printf("%lld\n",res);
}
return 0;
}

P4770 [NOI2018]你的名字的更多相关文章

  1. UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)

    NOI2019考前做NOI2018题.. 题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=5417 (luogu) http ...

  2. #P4770 [NOI2018]你的名字 的题解

    题目背景 实力强大的小A 被选为了ION2018 的出题人,现在他需要解决题目的命名问题. 题目描述 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外 ...

  3. 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]

    传送门 思路 按照套路,直接上后缀自动机. 部分分:\(l=1,r=|S|\) 首先把\(S\)和\(T\)的后缀自动机都建出来. 考虑枚举\(T\)中的右端点\(r\),查询以\(r\)结尾的串最长 ...

  4. luogu P4770 [NOI2018]你的名字

    传送门 upd 19.4.24: WC这个做法真的有问题,不往回跳会WA是因为一开始跳到了S[1...l-1]所对应的点,然后往后接字符的时候可能会因为不在正确的endpos中,然后往回跳过头,其实一 ...

  5. 洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)

    传送门 我有种自己根本没学过SAM的感觉……最后还是抄了老半天的题解…… 首先,对$S$和每一次的$T$都建一个SAM 先考虑一下$l=1,r=\left| S \right|$的情况 设$lim_i ...

  6. BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并

    题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...

  7. 【BZOJ5417】[NOI2018]你的名字(线段树,后缀自动机)

    [BZOJ5417][NOI2018]你的名字(线段树,后缀自动机) 题面 BZOJ 洛谷 题解 首先考虑\(l=1,r=|S|\)的做法,对于每次询问的\(T\)串,暴力在\(S\)串的\(SAM\ ...

  8. bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

  9. [NOI2018]你的名字(后缀自动机+线段树)

    题目描述 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了. 由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手 ...

随机推荐

  1. 什么是DDoS攻击?DDoS防御的11种方针详解

    对于遭受DDOS攻击的情况是让人很尴尬的,如果我们有良好的DDoS防御方法,那么很多问题就将迎刃而解,我们来看看我们有哪些常用的有效地方法来做好DDoS防御呢. 对于DDoS防御的理解: 对付DDOS ...

  2. VMware-workstation12.5.6 新建虚拟机 安装 centos6.5

    1.到官方下载镜像文件 注意:看到上面那两个红框不要着急复制链接用迅雷下载,因为点击进去是个页面,不知道官方做这个用意是怎样的,个人感觉很傻缺我们只要点击第一个红框就行 *************** ...

  3. Python拆分DataFrame

    # 在Python中可以根据某列的具体内容来拆分数据,保存成多个DataFrame! # 代码如下: ycsj = pfsj[pfsj['备注'].isin(['1'])] # 拆分数据: 结果:

  4. weblogic系列漏洞整理 -- 1. weblogic安装

    目录 0. 概述 1. 下载安装Java环境 2. 下载安装weblogic 安装 部署domain域 进入weblogic 3. 排错 如果出现如下错误 0. 概述 WebLogic是美国Oracl ...

  5. Apache POI导出excel表格

    项目中我们经常用到导出功能,将数据导出以便于审查和统计等.本文主要使用Apache POI实现导出数据. POI中文文档 简介 ApachePOI是Apache软件基金会的开放源码函式库,POI提供A ...

  6. 【HANA系列】SAP HANA XS使用Odata标志全解析

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA XS使用Oda ...

  7. EntityFramework Code-First 简易教程(八)-------一对一

    配置一对一(One-to-One)关系: 两个实体中,如果一个实体的一个实例与另一个实体相关,则我们就叫做一对一关系 查看如下代码: public class Student { public Stu ...

  8. 【待补充】[HDFS_3] HDFS 工作机制

    0. 说明 HDFS 初始化文件系统分析 && HDFS 文件写入流程 && HDFS 文件读取流程分析 有价值的相关文章: [漫画解读]HDFS存储原理 1. HDF ...

  9. Linux防火墙基础与编写防火墙规则

    Iptables采用了表和链的分层结构,每个规则表相当于内核空间的一个容器,根据规则集的不同用途划分为默认的四个表,raw表,mangle表,nat表,filter表,每个表容器内包括不同的规则链,根 ...

  10. tkinter学习系列(二)之窗口的设置

    目录 (一)窗体的最小框架 1.说明: 2.源代码: 3.实现效果: (二)窗体的基本设置 1.说明: 2.完整代码: 3.实现效果: (三)窗体的外形设置 1.说明: 2.完整代码: 3.实现效果: ...