Luogu4770 NOI2018你的名字(后缀数组+线段树)

  即求b串有多少个本质不同的非空子串,在a串的给定区间内未出现。即使已经8102年并且马上就9102年了,还是要高举SA伟大旗帜不动摇。

  考虑离线,将所有询问串及一开始给的串加分隔符连起来,求出SA。对于每个询问,我们对串的每个后缀,求出其在给定区间中最长的lcp是多少。这样就能得到不考虑本质不同时的答案,再考虑该串名次数组中相邻后缀的lcp去一下重即可。

  考虑怎么求这个最长的lcp。设该后缀在名次数组中的位置是i,给定区间是[l,r],这个东西实际上就是max{min{lcp(i..j),r-saj+1}} (saj>=l)。容易发现取min的两个函数实际上一个单减一个单增,于是容易想到的做法是二分答案,找到名次数组上对应区间,查询区间内是否有合法的sa值。但这样就是log^2了。实际上按sa倒序建可持久化线段树(或者仍然离线就只需要线段树了),线段树上二分查询即可。这个二分需要自底向上再自顶向下以保证复杂度,写起来感觉非常恶心。

  非常神奇的是这份代码在uoj过掉了,在luogu被卡常,在lojT成0分。

  upd:更神奇的是深夜在luogu交了一次就过掉了,而本来只有64。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 1600010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<''||c>'')) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
char s[N];
int len,n,m,a[N],sa[N],sa2[N],rk[N<<],tmp[N<<],cnt[N],h[N],f[][N],lg2[N],lcp[N],id[N];
ll ans[N];
struct data
{
int s,t,l,r,i;
bool operator <(const data&a) const
{
return l>a.l;
}
}q[N];
struct data2{int l,r,x;
}tree[N<<];
void make(int m)
{
for (int i=;i<=n;i++) cnt[rk[i]=a[i]]++;
for (int i=;i<=m;i++) cnt[i]+=cnt[i-];
for (int i=n;i>=;i--) sa[cnt[rk[i]]--]=i;
for (int k=;k<=n;k<<=)
{
int p=;
for (int i=n-k+;i<=n;i++) sa2[++p]=i;
for (int i=;i<=n;i++) if (sa[i]>k) sa2[++p]=sa[i]-k;
memset(cnt,,m+<<);
for (int i=;i<=n;i++) cnt[rk[i]]++;
for (int i=;i<=m;i++) cnt[i]+=cnt[i-];
for (int i=n;i>=;i--) sa[cnt[rk[sa2[i]]]--]=sa2[i];
memcpy(tmp,rk,sizeof(tmp));
p=;rk[sa[]]=;
for (int i=;i<=n;i++)
{
if (tmp[sa[i-]]!=tmp[sa[i]]||tmp[sa[i-]+k]!=tmp[sa[i]+k]) p++;
rk[sa[i]]=p;
}
m=p;if (m==n) break;
}
for (int i=;i<=n;i++)
{
h[i]=max(h[i-]-,);
while (a[i+h[i]]==a[sa[rk[i]-]+h[i]]) h[i]++;
}
for (int i=;i<=n;i++) f[][i]=h[sa[i]];
for (int i=;i<=n;i++)
{
lg2[i]=lg2[i-];
if ((<<lg2[i])<=i) lg2[i]++;
}
for (int j=;j<;j++)
for (int i=;i<=n;i++)
f[j][i]=min(f[j-][i],f[j-][min(n,i+(<<j-))]);
}
int query(int x,int y)
{
if (x==y) return N;x++;
return min(f[lg2[y-x+]][x],f[lg2[y-x+]][y-(<<lg2[y-x+])+]);
}
void build(int k,int l,int r)
{
tree[k].l=l,tree[k].r=r;tree[k].x=N;
if (l==r) {id[l]=k;return;}
int mid=l+r>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
}
int findl(int x,int r)
{
int ans=,k=id[x],c=tree[k].x;
while (k!=)
if (!(k&)) k>>=;
else if (r-min(c,tree[k-].x)+>=query(tree[k-].l,x)) {k>>=;break;}
else c=min(c,tree[k-].x),k>>=,ans=max(ans,min(query(tree[k].l,x),r-c+));
while (tree[k].l<tree[k].r&&tree[k].r>=x) k<<=;
while (tree[k].l<tree[k].r)
if (r-min(c,tree[k<<|].x)+>=query(tree[k<<|].l,x)) k=k<<|;
else k<<=,c=min(c,tree[k+].x),ans=max(ans,min(query(tree[k+].l,x),r-c+));
ans=max(ans,min(query(tree[k].l,x),r-min(tree[k].x,c)+));
return ans;
}
int findr(int x,int r)
{
int ans=,k=id[x],c=tree[k].x;
while (k!=)
if (k&) k>>=;
else if (r-min(c,tree[k+].x)+>=query(x,tree[k+].r)) {k>>=;break;}
else c=min(c,tree[k+].x),k>>=,ans=max(ans,min(query(x,tree[k].r),r-c+));
while (tree[k].l<tree[k].r&&tree[k].l<=x) k=k<<|;
while (tree[k].l<tree[k].r)
if (r-min(c,tree[k<<].x)+>=query(x,tree[k<<].r)) k<<=;
else k=k<<|,c=min(c,tree[k-].x),ans=max(ans,min(query(x,tree[k-].r),r-c+));
ans=max(ans,min(query(x,tree[k].r),r-min(tree[k].x,c)+));
return ans;
}
bool cmp(const int&a,const int&b)
{
return rk[a]<rk[b];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("name.in","r",stdin);
freopen("name.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
scanf("%s",s+);len=n=strlen(s+);
for (int i=;i<=n;i++) a[i]=s[i]-'a'+;
a[++n]=;
m=read();
for (int i=;i<=m;i++)
{
scanf("%s",s+);int t=strlen(s+);
q[i].s=n+;
for (int j=;j<=t;j++) a[++n]=s[j]-'a'+;
q[i].t=n;a[++n]=;
q[i].l=read(),q[i].r=read(),q[i].i=i;
}
sort(q+,q+m+);
make();
build(,,n);
q[].l=len+;
for (int i=;i<=m;i++)
{
for (int j=q[i-].l-;j>=q[i].l;j--)
for (int k=id[rk[j]];k;k>>=) tree[k].x=j;
for (int j=q[i].s;j<=q[i].t;j++) tmp[j]=j;
sort(tmp+q[i].s,tmp+q[i].t+,cmp);
for (int j=q[i].s;j<=q[i].t;j++) lcp[j]=max(findl(rk[tmp[j]],q[i].r),findr(rk[tmp[j]],q[i].r));
ans[q[i].i]=q[i].t-tmp[q[i].s]+-lcp[q[i].s];
for (int j=q[i].s+;j<=q[i].t;j++)
ans[q[i].i]+=q[i].t-tmp[j]+-max(lcp[j],query(min(rk[tmp[j]],rk[tmp[j-]]),max(rk[tmp[j]],rk[tmp[j-]])));
}
for (int i=;i<=m;i++) printf(LL,ans[i]);
return ;
}
上一篇:MySQL用户管理和权限设置


下一篇:实验吧之【who are you?】(时间盲注)