传送门

upd 19.4.24:

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

Orz @Itst ddw


这题写死我了,因为一点点鬼会注意到的细节调好久,,,

先考虑前17个点,如果我们不考虑T的不同子串限制,那么就是把T放在S的SAM上匹配,答案为\(\sum_{i=1}^{|T|}i-l[i]\)(匹配长度).考虑怎么判重,对T也构建一个SAM,由于每个前缀的贡献答案的子串左端点在\([1,i-l[i])\)之间,对应到parent树上就是若干条链,那么每个位置的贡献就是对应链的没有被前面的链覆盖的部分

后面的点,首先我们每次要跳到S[1...l-1]所对应的点,然后把T放在上面匹配,不过可能匹配过程中,S匹配上的子串没有在[l,r]之间的,这时候我们要跳parent,去掉匹配子串的前面一些部分,使得[l,r]之间出现对应的串.这个可以通过SAM每个节点开一棵线段树维护endpos集合,通过线段树合并维护,每次查询对应状态[l,r]中最大的结束位置-匹配长度+1是否\(\ge l\)

(这种做法有点问题,可能跳parent会因为串的前面部分而跳过头,导致匹配长度变小,从而导致答案变大,所以跳完后要尝试往回跳orz)

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define db double using namespace std;
const int N=1000000+10;
il LL rd()
{
LL x=0,w=1;char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
char cc[N],ss[N];
int n,m;
int an[N>>1],bk[N>>1],aa[N],loc[N>>1],st[N],tp;
int s[N*22],ch[N*22][2],rt[N],tot;
il void inst(int o1,int o2,int x)
{
int l=1,r=n;
s[o1]=max(s[o2],x);
while(l<r)
{
int mid=(l+r)>>1;
if(x<=mid)
{
ch[o1][0]=++tot,ch[o1][1]=ch[o2][1];
o1=ch[o1][0],o2=ch[o2][0];
r=mid;
}
else
{
ch[o1][0]=ch[o2][0],ch[o1][1]=++tot;
o1=ch[o1][1],o2=ch[o2][1];
l=mid+1;
}
s[o1]=max(s[o2],x);
}
}
int merge(int o1,int o2)
{
if(!o1||!o2) return o1+o2;
//if(o1==o2) return o1;
int o=++tot;
s[o]=max(s[o1],s[o2]);
ch[o][0]=merge(ch[o1][0],ch[o2][0]),ch[o][1]=merge(ch[o1][1],ch[o2][1]);
return o;
}
int quer(int o,int l,int r,int ll,int rr)
{
if(!o) return s[0];
if(ll<=l&&r<=rr) return s[o];
int mid=(l+r)>>1,an=s[0];
if(ll<=mid) an=/*max(an,*/quer(ch[o][0],l,mid,ll,rr)/*)*/;
if(rr>mid) an=max(an,quer(ch[o][1],mid+1,r,ll,rr));
return an;
}
struct SAM
{
int fa[N],len[N],a[N],la,tt;
int ch[N][26];
bool v[N];
SAM(){la=tt=1;}
il void inst(char c,int id)
{
int np=++tt,p=la;
la=np,a[np]=id,len[np]=len[p]+1;
while(p&&!ch[p][c-'a']) ch[p][c-'a']=np,p=fa[p];
if(!p) fa[np]=1;
else
{
int q=ch[p][c-'a'];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tt;
fa[nq]=fa[q],len[nq]=len[p]+1,fa[q]=fa[np]=nq;
for(int i=0;i<26;++i) ch[nq][i]=ch[q][i];
while(p&&ch[p][c-'a']==q) ch[p][c-'a']=nq,p=fa[p];
}
}
}
il void clear()
{
memset(ch,0,4*26*(tt+2));
memset(v,0,1*(tt+2));
memset(fa,0,4*(tt+2));
memset(len,0,4*(tt+2));
la=tt=1;
}
}a,b; int main()
{
scanf("%s",ss+1);
n=strlen(ss+1);
for(int i=1;i<=n;++i) a.inst(ss[i],i);
for(int i=1;i<=a.tt;++i) ++bk[a.len[i]];
for(int i=1;i<=n;++i) bk[i]+=bk[i-1];
for(int i=1;i<=a.tt;++i) aa[bk[a.len[i]]--]=i;
memset(s,-63,sizeof(s));
for(int i=a.tt,las;i;--i)
{
if(a.a[aa[i]]) las=rt[aa[i]],inst(rt[aa[i]]=++tot,las,a.a[aa[i]]);
rt[a.fa[aa[i]]]=merge(rt[a.fa[aa[i]]],rt[aa[i]]);
}
loc[0]=1;
for(int i=1,nw=1;i<=n;++i) loc[i]=nw=a.ch[nw][ss[i]-'a'];
int q=rd();
while(q--)
{
scanf("%s",cc+1);
m=strlen(cc+1);
LL ans=0;
int l=rd(),r=rd();
for(int i=1,nw=loc[l-1],kk=0;i<=m;++i)
{
int w=cc[i]-'a';
while(nw&&!a.ch[nw][w]) nw=a.fa[nw],kk=a.len[nw];
if(!nw) nw=1,kk=0;
else nw=a.ch[nw][w],++kk;
tp=0;
while(nw&&quer(rt[nw],1,n,l,r)-kk+1<l)
st[++tp]=nw,nw=a.fa[nw],kk=a.len[nw];
if(tp)
{
while(quer(rt[nw],1,n,l,r)-kk+1>=l) ++kk,nw=(kk<=a.len[nw])?nw:st[tp--];
--kk,nw=(kk>a.len[a.fa[nw]])?nw:a.fa[nw];
}
an[i]=kk;
}
b.clear();
for(int i=1;i<=m;++i) b.inst(cc[i],0);
b.v[0]=1;
for(int i=1,nw=1;i<=m;++i)
{
int w=cc[i]-'a',p=nw=b.ch[nw][w];
while(!b.v[p]&&b.len[p]>=an[i]) ans+=b.len[p]-max(b.len[b.fa[p]],an[i]),b.v[p]=1,p=b.fa[p];
}
printf("%lld\n",ans);
}
return 0;
}

luogu 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. P4770 [NOI2018]你的名字

    蒟蒻表示不会sam凉凉了,所以只能提高SA技巧? 题意:有一个串\(A\),每次选择一个\(A\)的子串\(A'\),以及串\(B\),问\(B\)的所有本质不同子串中不在\(A'\)中的串的数量. ...

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

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

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

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

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

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

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

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

  9. [NOI2018]你的名字(68pts)

    SAM真让人头秃. 题面 https://www.luogu.org/problemnew/show/P4770 首先考虑 l=1,r=∣S∣的做法 如果对于ION2018的子串不用判重的话,对ION ...

随机推荐

  1. SSH出现Connection refused错误

    笔者在使用ssh localhost时出现Connection refused提示,初步判断是22端口未开启服务,也有可能是防火墙未正确配置. 测试 使用netstat | grep 22测试,发现2 ...

  2. 纯原生JS大图轮播

    CSS部分: CSS: <style type="text/css"> #banner { position: relative; width: 500px; heig ...

  3. (转)轻松学,Java 中的代理模式及动态代理

    背景:讲到反射机制,肯定会想到动态代理. 轻松学,Java 中的代理模式及动态代理 代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强.值得注意的是,代理类和被代理类应该 ...

  4. mysql视图和临时表的区别

    视图 视图是由从数据库的基本表中选出来的数据组成的逻辑窗口,它与基本表不同的是,视图是一个虚表.数据库中只存放视图的定义,而不存放视图包含的数据,这些数据仍存放在原来的基表中.所以基表中的数据如果发生 ...

  5. jquery.form.js ajax提交上传文件

    项目中最近有用到表单提交,是带有图片上传的表单录入,需要ajax异步提交,网上找了好多例子都是只能提交上传字段一个信息的,这里整理一下.表单里有普通文本信息字段也有图片上传字段. 1.jsp代码--引 ...

  6. mac java_home等系统参数配置

    JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/HomeCLASSPAHT=.:$JAVA_HOME/lib ...

  7. SQL Server索引的作用

    一.深入浅出理解索引结构 实际上,您可以把索引理解为一种特殊的目录.微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引.簇集索引)和非聚集索引(noncl ...

  8. C++: typedef与template的配合使用;

    利用STL的vector能够实现多维矩阵,但是写起来不怎么好看,使用typedef定位为 固定的格式: //多维矩形,vector实现: template<class T> class i ...

  9. Linux记录-定时crontab

    /etc/crontab文件和crontab -e命令区别 1.格式不同 分 时 日 月 星期 要运行的命令 第1列分钟1-59 第2列小时1-23(0表示子夜) 第3列日1-31 第4列月1-12 ...

  10. JAVA-try-catch-finally-自定义异常例子(适合初学者)

    package com.net.xinfang.reflect; import java.io.IOException; import java.util.Scanner; /*** * 运行try块 ...