您的位置:首页 > 其它

[JZOJ5027]【NOI2017模拟3.25】历史行程

2017-03-29 22:08 405 查看

题目大意

给一个长度为n的01字符串s,还有m个询问,每个询问有两个数l,r,问s的前缀s[1..l],s[1..l+1]…s[1..r]中的任意两个前缀的最长公共后缀是多少。

n,m<=100000

分析

首先可以把s反过来,这样就是问后缀的lcp了,询问记得也要反过来。

考虑弄出一个sa。

对于一个询问[l,r],答案是什么呢?

就是rank[l]..rank[r]按大小排后,即l~r所有位置按照sa的顺序来排之后,每个相邻两个之间的lcp的最大值嘛。

即:

令a[1~r-l+1]=rank[l~r],

sort(a);

令height[i]为sa[i]与sa[i+1]的lcp

则ans=max(min(height[a[i]],height[a[i]+1]...height[a[i+1]−1]))

那么我们就学会了暴力。

学会了暴力,就上莫队吧。

现在问题来了,当我们新插入一个位置x之后,我们需要在原有的位置集合中,找到rank与rank[x]最相邻的两个位置,然后再更新答案。直接做的话怎么找都要带个log。

如何消去log?

机智法:

一个的结论:假如你有一个链表,维护一个元素左右两个相邻元素是谁。在删除了一些元素后,如果要回复原来的状态,按照原来删的顺序反过来加入,同时利用之前被删除的点的信息,可以o(1)恢复。如图:



对于i,j,k三个点,假如我先删除了j,那么right[i]=right[j],left[k]=left[j],j的东西不管。

然后我要恢复j,那么只需要再次利用left[j],right[j],即可恢复原来状况。

推广到多个点,删除多次是一样的。但必须要按顺序来。

回到题目,设rans[i]表示当询问左端点在某一个询问块中,(设块尾位置为x),对于虚拟询问(x+1,i)的答案。

对于同一块内的询问,我们按右端点从大到小排,这样我们右端点就一直只会删除,为顺序提供保障。

我们对于一个询问:

1,假如它跟上一个询问的块不一样,我们把整一个链表重构,重构1~n所有元素,现在我们拥有一个l=1,r=n的区间[l,r]的信息。然后逐个删,左边l先删到块尾+1,右边r接着删到块尾+1,然后再重新加入右边至n,更新rans数组,最后再把l弄回块开头。

2,现在我们求答案。首先r调整至询问右端点。用rans[r]更新此询问的答案。现在相当于我把询问从块尾x切开,已经求了右边了,按理来说,此时询问的当前答案对应的是(x+1,r)这个询问的答案,我们只要把x到左端点逐个插回来更新就好了。我们l此时是在块开头,我们删除到结尾再加回去就可以求出来了。当然了,如果右端点已经小于x+1了,就只需要删除到右端点再插回去,rans也用不着了。

上面写得很繁杂,反正抓主线就是,我们要保证调整过的维护的序列,要能调回去再求其他询问的答案,即要严格保证插入删除顺序不会破坏原来链表的结构。

min(height[])用rmq搞就o(1)了。

总时间复杂度:O(nn√+nlog2n)

小细节

就是要注意询问的右端点小于块尾的情况。排序别排错了。

SA要多用,有点生疏了。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int N=100005;
struct rec
{
int l,r,bel,id;
}que
;
int sa
,rank
,height
,ws
,x
,y
,wv
,rmq
[20];
int l,r,rans
,ans,prt
,le
,ri
,Log
;
int i,j,k,n,m,siz,q,st,en,id;
char s
;
bool cmp(rec a,rec b)
{
return a.bel<b.bel||(a.bel==b.bel&&a.r>b.r);
}
void getsa()
{
fo(i,1,n) ws[x[i]]++;
ws[2]+=ws[1];
fd(i,n,1)
sa[ws[x[i]]--]=i;
j=1;
while (j<n)
{
q=0;
fo(i,n-j+1,n)
y[++q]=i;
fo(i,1,n) if (sa[i]>j)
y[++q]=sa[i]-j;
fo(i,1,n) wv[i]=x[y[i]];
fo(i,1,n) ws[i]=0;
fo(i,1,n) ws[x[i]]++;
fo(i,1,n) ws[i]+=ws[i-1];
fd(i,n,1) sa[ws[wv[i]]--]=y[i];
fo(i,1,n) y[i]=x[i];
x[sa[1]]=1;
q=1;
fo(i,2,n)
{
if (y[sa[i]]!=y[sa[i-1]]||y[sa[i]+j]!=y[sa[i-1]+j])
q++;
x[sa[i]]=q;
}
j*=2;
}
}
void getheight()
{
fo(i,1,n) rank[sa[i]]=i;
int k=0;
fo(i,1,n)
{
if (k>0) k--;
while (i+k<=n&&sa[rank[i]+1]+k<=n&&s[i+k]==s[sa[rank[i]+1]+k]) k++;
height[rank[i]]=k;
}
}
void getrmq()
{
fo(i,1,n) rmq[i][0]=height[i];
fo(j,1,Log
)
fo(i,1,n)
rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);
}
int getmin(int x,int y)
{
if (x<=0||y<=0) return 0;
int z=Log[y-x+1];
return min(rmq[x][z],rmq[y-(1<<z)+1][z]);
}
void restruct(int i)
{
fo(i,1,n)
{
le[sa[i]]=sa[i-1];
ri[sa[i]]=sa[i+1];
}
l=1;
r=n;
for(;l<en+1;l++)
{
ri[le[l]]=ri[l];
le[ri[l]]=le[l];
}
for(;r>en+1;r--)
{
ri[le[r]]=ri[r];
le[ri[r]]=le[r];
}
rans[r]=0;
for(;r<n;r++)
{
le[ri[r+1]]=r+1;
ri[le[r+1]]=r+1;
rans[r+1]=max(rans[r],max(getmin(rank[le[r+1]],rank[r+1]-1),getmin(rank[r+1],rank[ri[r+1]]-1)));
}
for(;l>st;l--)
{
le[ri[l-1]]=l-1;
ri[le[l-1]]=l-1;
}
}
int main()
{
freopen("history.in","r",stdin);
freopen("history.out","w",stdout);
scanf("%d %d\n",&n,&m);
scanf("%s",s+1);
fo(i,1,n/2)
swap(s[i],s[n-i+1]);
fo(i,1,n)
x[i]=s[i]-'0'+1;
siz=trunc(sqrt(n));
fo(i,1,m)
{
scanf("%d %d",&que[i].l,&que[i].r);
r=n-que[i].l+1;
que[i].l=n-que[i].r+1;
que[i].r=r;
que[i].bel=(que[i].l-1)/siz+1;
que[i].id=i;
}
sort(que+1,que+1+m,cmp);
fo(i,0,n) Log[i]=trunc(log(i)/log(2));
getsa();
getheight();
getrmq();
fo(i,1,m)
{
st=(que[i].bel-1)*siz+1;
en=st+siz-1;
id=que[i].id;
if (que[i].bel!=que[i-1].bel)
restruct(i);
for(;r>que[i].r;r--)
{
ri[le[r]]=ri[r];
le[ri[r]]=le[r];
}
for(;l<=en&&l<que[i].r;l++)
{
ri[le[l]]=ri[l];
le[ri[l]]=le[l];
}
if (que[i].r>en)prt[id]=rans[que[i].r];
for(;l>st;l--)
{
if (l>que[i].l)
prt[id]=max(prt[id],max(getmin(rank[le[l-1]],rank[l-1]-1),getmin(rank[l-1],rank[ri[l-1]]-1)));
le[ri[l-1]]=l-1;
ri[le[l-1]]=l-1;
}
}
fo(i,1,m) printf("%d\n",prt[i]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: