1. 程式人生 > >bzoj 2565: 最長雙迴文串【manacher+線段樹】

bzoj 2565: 最長雙迴文串【manacher+線段樹】

因為我很愚蠢所以用了很愚蠢的O(nlogn)的manacher+線段樹做法
就是開兩個線段樹mn和mx分別表示左端點在i的最長迴文子串和右端點在i的最長迴文子串
用manacher求出每個點的最長迴文子串,然後對於一組(i,f[i])(這裡的i是加完#之後的串),我們考慮對原串貢獻是對於中點右邊一段迴文串上點x的mn貢獻i-2x+1,x最後加就變成線上段樹上貢獻i+1,然後同理對左邊一段貢獻2x-i+1,線上段樹上貢獻-i+1,注意這裡要分一下奇偶還有仔細算一下邊界
然後列舉斷點,線上段樹上查,取max即可
實際上注意到是可以O(n)的,mnmx更新時候的範圍超過之後就變成負的沒有意義了,所以直接更新區間端點,最後把i%2相同的向前/向後更新一下即可
或者直接用迴文自動機預處理

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200005;
int n,f[N],ans;
char c[N],s[N];
struct xds
{
    int l,r,mx,lz;
}t[N<<2];
struct wk
{
    xds t[N<<2];
    void build(int ro,int l,int r)
    {
        t[ro].l=l,t[ro].r=r,t[ro].lz=t[ro].mx=-1e9;
        if(l==r)
            return;
        int mid=(l+r)>>1;
        build(ro<<1,l,mid);
        build(ro<<1|1,mid+1,r);
    }
    void pd(int ro)
    {
        if(t[ro].lz!=-1e9)
        {
            t[ro<<1].mx=max(t[ro<<1].mx,t[ro].lz);
            t[ro<<1].lz=max(t[ro<<1].lz,t[ro].lz);
            t[ro<<1|1].mx=max(t[ro<<1|1].mx,t[ro].lz);
            t[ro<<1|1].lz=max(t[ro<<1|1].lz,t[ro].lz);
            t[ro].lz=-1e9;
        }
    }
    void update(int ro,int l,int r,int v)
    {//cerr<<l<<" "<<r<<endl;
        if(l>r)
            return;
        if(t[ro].l==l&&t[ro].r==r)
        {
            t[ro].mx=max(t[ro].mx,v);
            t[ro].lz=max(t[ro].lz,v);
            return;
        }
        pd(ro);
        int mid=(t[ro].l+t[ro].r)>>1;
        if(r<=mid)
            update(ro<<1,l,r,v);
        else if(l>mid)
            update(ro<<1|1,l,r,v);
        else
            update(ro<<1,l,mid,v),update(ro<<1|1,mid+1,r,v);
        t[ro].mx=max(t[ro<<1].mx,t[ro<<1|1].mx);
    }
    int ques(int ro,int p)
    {
        if(t[ro].l==t[ro].r)
            return t[ro].mx;
        pd(ro);
        int mid=(t[ro].l+t[ro].r)>>1;
        if(p<=mid)
            return ques(ro<<1,p);
        else
            return ques(ro<<1|1,p);
    }
}mn,mx;
int main()
{
    scanf("%s",c+1);
    n=strlen(c+1);
    for(int i=1;i<=n;i++)
        s[2*i]=c[i],s[2*i+1]='#';
    s[0]='$',s[1]='#',s[2*n+2]='%';
    int mxw=0,w;
    mn.build(1,1,n),mx.build(1,1,n);
    for(int i=1;i<2*n+2;i++)
    {
        if(i<mxw)
            f[i]=min(f[2*w-i],mxw-i);
        else
            f[i]=1;
        for(;s[i+f[i]]==s[i-f[i]];f[i]++);
        if(i+f[i]>mxw)
            mxw=i+f[i],w=i;
        if(s[i]=='#')
            mx.update(1,max(1,(i+1)/2),min(n,(i+1)/2+(f[i]-1)/2-1),-i+1),mn.update(1,max(1,(i-1)/2-(f[i]-1)/2+1),min(n,(i-1)/2),i+1);
        else
            mx.update(1,max(1,i/2),min(n,i/2+f[i]/2-1),-i+1),mn.update(1,max(1,i/2-f[i]/2+1),min(n,i/2),i+1);
    }
    for(int i=2;i<=n;i++)
        ans=max(ans,mx.ques(1,i-1)+2*(i-1)+mn.ques(1,i)-2*i);//,cerr<<i<<" "<<mn.ques(1,i)-2*i<<" "<<mx.ques(1,i)+2*i<<endl;;
    printf("%d\n",ans);
    return 0;
}