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

题目大意

给一个长度为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)恢复。如图:
[JZOJ5027]【NOI2017模拟3.25】历史行程_第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
#include
#include
#include
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[N];
int sa[N],rank[N],height[N],ws[N],x[N],y[N],wv[N],rmq[N][20];
int l,r,rans[N],ans,prt[N],le[N],ri[N],Log[N];
int i,j,k,n,m,siz,q,st,en,id;
char s[N];
bool cmp(rec a,rec b)
{
    return a.belb.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 (jq=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[n])
        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]);
}
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(;l1;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(;r1]]=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&&lif (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]);
}

你可能感兴趣的:(后缀数组,分块,暴力,根号算法等,搜索,暴力)