luogu P4770 [NOI2018]你的名字
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]你的名字的更多相关文章
- UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)
NOI2019考前做NOI2018题.. 题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=5417 (luogu) http ...
- #P4770 [NOI2018]你的名字 的题解
题目背景 实力强大的小A 被选为了ION2018 的出题人,现在他需要解决题目的命名问题. 题目描述 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外 ...
- 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]
传送门 思路 按照套路,直接上后缀自动机. 部分分:\(l=1,r=|S|\) 首先把\(S\)和\(T\)的后缀自动机都建出来. 考虑枚举\(T\)中的右端点\(r\),查询以\(r\)结尾的串最长 ...
- P4770 [NOI2018]你的名字
蒟蒻表示不会sam凉凉了,所以只能提高SA技巧? 题意:有一个串\(A\),每次选择一个\(A\)的子串\(A'\),以及串\(B\),问\(B\)的所有本质不同子串中不在\(A'\)中的串的数量. ...
- 洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)
传送门 我有种自己根本没学过SAM的感觉……最后还是抄了老半天的题解…… 首先,对$S$和每一次的$T$都建一个SAM 先考虑一下$l=1,r=\left| S \right|$的情况 设$lim_i ...
- bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...
- BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并
题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...
- 【BZOJ5417】[NOI2018]你的名字(线段树,后缀自动机)
[BZOJ5417][NOI2018]你的名字(线段树,后缀自动机) 题面 BZOJ 洛谷 题解 首先考虑\(l=1,r=|S|\)的做法,对于每次询问的\(T\)串,暴力在\(S\)串的\(SAM\ ...
- [NOI2018]你的名字(68pts)
SAM真让人头秃. 题面 https://www.luogu.org/problemnew/show/P4770 首先考虑 l=1,r=∣S∣的做法 如果对于ION2018的子串不用判重的话,对ION ...
随机推荐
- 上pixiv解决法(保存)
C:\Windows\System32\drivers\etc\hosts 127.0.0.1 localhost 127.0.0.1 advstat.xunlei.com 127.0.0.1 cl. ...
- python3+django2 开发易语言网络验证(下)
第六步:网络验证服务器端项目上线部署 功夫不负有心人,终于部署成功啦! 前期准备: 项目名:netauth 系统:百度云服务器下的Ubuntu16.4 软件:xshell(无论如何想办法用这个跟服务器 ...
- crond守护进程实现定时监控某进程占有内存的大小
1)添加计划任务 crontab -e会使用某个编辑器打开某个文件,然后在内输入需要执行的计划任务,保存后在/var/spool/cron/crontabs/下会出现以用户名命名的文件 2)计划任务如 ...
- 测试唯一ID支持多大的并发量
昨天突然考虑到这个问题,在并发比较大的情况下,你用于生成唯一ID的函数是否还能正常运行?也就是说比如我一下子进来40000个订单,你需要生成不重复的订单ID吧? 对于这个问题我以前没考虑过,但是可能是 ...
- prototype 与 proto的关系是什么:
__proto__是什么? 我们在这里简单地说下.每个对象都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性 时,如果这个对象内部不存在这个属性,那么他就会去__proto_ ...
- Game HDU - 3389 (博弈论)
Bob and Alice are playing a new game. There are n boxes which have been numbered from 1 to n. Each b ...
- (线性dp 最大子段和 最大子矩阵和)POJ1050 To the Max
To the Max Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 54338 Accepted: 28752 Desc ...
- Vue(基础七)_webpack(CommonsChunkPlug的使用)
---恢复内容开始--- 一.前言 1.多入口文件配置 2.CommonsChunkPlugin的用法 ...
- M1-SaltStack&Flask-Day4
1.virtualenv 虚拟环境 2.virtualenv env1 -p= 解释器路径 3. 进入Scripts 执行activate 激活配置 4.执行deactivate 取消激活配置 2.1 ...
- hibernate注解方式来处理映射关系
在hibernate中,通常配置对象关系映射关系有两种,一种是基于xml的方式,另一种是基于annotation的注解方式,熟话说,萝卜青菜,可有所爱,每个人都有自己喜欢的配置方式,我在试了这两种方式 ...