蒟蒻表示不会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. (网页)logback的使用和logback.xml详解(转)

    转自博客园:行走在云端的愚公: 一.logback的介绍 Logback是由log4j创始人设计的另一个开源日志组件,官方网站: http://logback.qos.ch.它当前分为下面下个模块: ...

  2. 线程:生命周期、实现方式、start()和run()的区别!

    1.线程的生命周期 要想实现多线程,必须在主线程中创建新的线程对象.Java语言使用Thread类及其子类的对象来表示线程,在它的 一个完整的生命周期中通常要经历如下的五种状态: 新建:当一个Thre ...

  3. HashTree【转】

    http://blog.csdn.net/yang_yulei/article/details/46337405 在各种数据结构(线性表.树等)中,记录在结构中的相对位置是随机的.因此在机构中查找记录 ...

  4. ws协议的配置

    server { listen 80; server_name 域名或IP; rewrite ^(.*)$ https://$host$1 permanent; } server { listen 4 ...

  5. Mac上用spotlight搜索输入几个字母后闪退

    最近使用电脑时遇到的问题: 使用spotlight进行搜索时,只要输入字母超过一定个数(在我的Mac上是3个),spotlight就闪退了. 谷歌搜索得到大部分解决方案是在系统自带词典的偏好设置里取消 ...

  6. AD域自定义属性《完整》

    1.安装Active Directory 架构,下载:adminpak.msi安装. 2.以管理员运行cmd,执行:regsvr32 schmmgmt.dll(该命令将在计算机上注册“schmmgmt ...

  7. Windows结构化异常处理浅析

    近期一直被一个问题所困扰,就是写出来的程序老是出现无故崩溃,有的地方自己知道可能有问题,但是有的地方又根本没办法知道有什么问题.更苦逼的事情是,我们的程序是需要7x24服务客户,虽然不需要实时精准零差 ...

  8. wordpress使用rsync加openvpn进行同步和备份

    目录 wordpress使用rsync加openvpn进行同步和备份 环境 普通同步策略的弊端 改良的同步方案 从头细说起 备份数据 打包wordpress项目和备份 服务端安装rsync 服务端配置 ...

  9. CentOS7搭建OpenVPN

    目录 CentOS7搭建OpenVPN 环境 安装 第一步.安装openvpn及所需软件 第二步.编辑vars文件,根据自己环境配置 第三步.创建服务端证书及key 第四步.创建客户端证书 第五步.拷 ...

  10. SQL mysql优化

    慢查询 如何通过慢查日志发现有问题的SQL? 查询次数多且每次查询占用时间长的SQL pt-query-digest分析前几个查询 IO大的SQL pt-query-diges分析中的Rows exa ...