1. 程式人生 > >P2463 [SDOI2008]Sandy的卡片

P2463 [SDOI2008]Sandy的卡片

寫一種\(O(nm)\)的做法,也就是\(O(\sum 串長)\)的。

先通過差分轉化,把每個數變成這個數與上一個數的差,第一個數去掉,答案就是最長公共子串+1

按照套路把所有串拼起來,中間加一個分隔符號,然後用DC3求出SA以及height

(DC3我也不會,蒯的TJJ的板子,因為一般用倍增SA足夠了)

對每個字尾可以知道它在原來的第幾個串,那麼求出SA以後,答案就是一段區間\([l,r]\),即排名為\([l,r]\)的這些字尾,而且必須覆蓋原來的所有串,長度就是這些字尾的LCP,就是一段height的最小值

列舉左端點,那麼最小的右端點一定不小於上一個的最小右端點,所以直接維護右端點以及這一段區間包括原來每一個串的數量就好了

最小值可以直接單調佇列維護

#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
typedef unsigned long long ulint;
namespace input
{
    const int bufl = 1<<14;
    char buf[bufl],*s=buf,*t=buf;
    inline int fetch()
    {
        if(s==t){t=(s=buf)+fread(buf,1,bufl,stdin);if(s==t)return EOF;}
        return *s++;
    }
    inline int gi()
    {
        register int a=0,b=1,c=fetch();
        while(!isdigit(c))b^=c=='-',c=fetch();
        while(isdigit(c))a=a*10+c-48,c=fetch();
        return b?a:-a;
    }
}
using input::gi;
const int N = 202007 , N3 = N*3 , maxalpha = 99999;
int wa[N3],wb[N3],wv[N3],buc[N3];
inline void bsort(int *s,int *a,int *b,int n,int m)
{
    register int i;
    for(i=0;i<n;i++)wv[i]=s[a[i]];
    for(i=0;i<m;i++)buc[i]=0;
    for(i=0;i<n;i++)buc[wv[i]]++;
    for(i=1;i<m;i++)buc[i]+=buc[i-1];
    for(i=n-1;i>=0;i--)b[--buc[wv[i]]]=a[i];
}
inline int c0(int *s,int a,int b)
{
    return s[a]==s[b] && s[a+1]==s[b+1] && s[a+2]==s[b+2];
}
inline int c12(int k,int *s,int a,int b)
{
    if(k==2)return s[a]<s[b] || (s[a]==s[b] && c12(k-1,s,a+1,b+1));
    return s[a]<s[b] || (s[a]==s[b] && wv[a+1]<wv[b+1]);
}
#define f(x) ((x)/3+((x)%3==1?0:tb))
#define g(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
void dc3(int *s,int *sa,int n,int m)
{
    int i,j,*sn=s+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
    s[n]=s[n+1]=0;
    for(i=0;i<n;i++)if(i%3!=0)wa[tbc++]=i;
    bsort(s+2,wa,wb,tbc,m),bsort(s+1,wb,wa,tbc,m),bsort(s,wa,wb,tbc,m);
    for(sn[f(wb[0])]=0,p=1,i=1;i<tbc;i++)sn[f(wb[i])]=c0(s,wb[i-1],wb[i])?p-1:p++;
    if(p<tbc)dc3(sn,san,tbc,p);
    else for(i=0;i<tbc;i++)san[sn[i]]=i;
    for(i=0;i<tbc;i++)if(san[i]<tb)wb[ta++]=san[i]*3;
    if(n%3==1)wb[ta++]=n-1;
    bsort(s,wb,wa,ta,m);
    for(i=0;i<tbc;i++)wv[wb[i]=g(san[i])]=i;
    for(i=j=p=0;i<ta && j<tbc;p++)sa[p]=c12(wb[j]%3,s,wa[i],wb[j])?wa[i++]:wb[j++];
    for(;i<ta;p++)sa[p]=wa[i++];
    for(;j<tbc;p++)sa[p]=wb[j++];
}
#undef f
#undef g
void calheight(int *s,int *sa,int n,int *rk,int *ht)
{
    register int i,j,k=0;
    for(i=1;i<=n;++i)rk[sa[i]]=i;
    for(i=0;i<n;ht[rk[i++]]=k)for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k] && k<n;++k);
    return;
}
char s[N3];
int n,ss[N3],sa[N3],rk[N3],ht[N3];
int p[N3],S[N3];
int que[N3],hd,tl,cnt[N3];
int main()
{
    int NN=gi(),n=0,sep=0;
    if(NN==1)return printf("%d\n",gi()),0;
    while(NN--){
        int t=gi(),lst=gi();
        while(--t){
            int x=gi();
            S[n++]=lst-x+10000;
            lst=x;
        }
        S[n++]=++sep;
    }
    s[n]=0;
    dc3(S,sa,n+1,20000);
    calheight(S,sa,n,rk,ht);
    int k=1;
    for(int i=0;i<n;++i)if(S[i]>1000)p[i]=k;else ++k;//p[i]是字尾i屬於原來的第幾個串
    int CNT=0,r=k,Mx=0;//cnt是原來每個串的出現次數,CNT是總的
    hd=tl=0;
    --k;
    for(int i=k+1;i<=n;++i){
        while(r<=n&&CNT<k){
            if(cnt[p[sa[r]]]++==0)++CNT;
            if(r){
                while((hd^tl)&&ht[que[tl-1]]>ht[r])--tl;
                que[tl++]=r;
            }
            ++r;
        }
        if(CNT<k)break;
        while((hd^tl)&&que[hd]<=i)++hd;
        if(hd^tl)Mx=std::max(Mx,ht[que[hd]]);
        if(--cnt[p[sa[i]]]==0)--CNT;
    }
    printf("%d\n",Mx+1);
    return 0;
}

(大括號換行的是蒯的板子,不換行的也就是核心程式碼是我自己寫的,DC3只是為了保證複雜度而已)