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;
}
上一篇:手动搭建的react环境中,关于图片引入的问题


下一篇:Linux手动搭建LAMP环境