【BZOJ4698】Sdoi2008 Sandy的卡片 后缀数组+RMQ

【BZOJ4698】Sdoi2008 Sandy的卡片

Description

Sandy和Sue的热衷于收集干脆面中的卡片。然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片兑换超炫的人物模型。每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型,首先必须要集够N张卡片,对于这N张卡片,如果他们都有一个相同的子串长度为k,则可以兑换一个等级为k的人物模型。相同的定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串。Sandy的卡片数远远小于要求的N,于是Sue决定在Sandy的生日将自己的卡片送给Sandy,在Sue的帮助下,Sandy终于集够了N张卡片,但是,Sandy并不清楚他可以兑换到哪个等级的人物模型,现在,请你帮助Sandy和Sue,看看他们最高能够得到哪个等级的人物模型。

Input

第一行为一个数N,表示可以兑换人物模型最少需要的卡片数,即Sandy现在有的卡片数
第i+1行到第i+N行每行第一个数为第i张卡片序列的长度Mi,之后j+1到j+1+Mi个数,用空格分隔,分别表示序列中
的第j个数
n<=1000,M<=1000,2<=Mi<=101

Output

一个数k,表示可以获得的最高等级。

Sample Input

2
2 1 2
3 4 5 9

Sample Output

2

题解:本题让我们求的是变化趋势相同的子串,那么我们直接求出每个数串相邻两项的差,然后用这个差来求最长公共子串就行了。当然,这些差可能会有负值,那就集体减去最小值-1(防止出现0)就行了。先把这些串全部连接起来,中间用极大值隔开,求出sa和height。然后再height数组上维护这样一个区间,使得这个区间中的后缀在每个数串中都出现过,并且区间长度尽可能短,然后从左向右平移这个区间,用RMQ维护区间中最小的height,答案就是这个最大的最小值+1。当然这题也可以二分答案来做。

因为RMQ写错竟然调了一天,感觉我有几年没写RMQ了。

 

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=200010;
int minn,maxx;
int r[maxn],ra[maxn],rb[maxn],st[maxn],sa[maxn],rank[maxn],h[maxn];
int N,M[maxn],sm[maxn],bel[maxn],n,m,v[maxn];
int s[maxn],sum,ans;
int dp[maxn][20],Log[maxn];
void work()
{
    int i,j,k,*x=ra,*y=rb,p;
    for(i=0;i<n;i++)    st[x[i]=r[i]]++;
    for(i=1;i<m;i++)    st[i]+=st[i-1];
    for(i=n-1;i>=0;i--)    sa[--st[x[i]]]=i;
    for(j=p=1;p<n;j<<=1,m=p)
    {
        for(i=n-j,p=0;i<n;i++)    y[p++]=i;
        for(i=0;i<n;i++)    if(sa[i]>=j)    y[p++]=sa[i]-j;
        for(i=0;i<m;i++)    st[i]=0;
        for(i=0;i<n;i++)    st[x[y[i]]]++;
        for(i=1;i<m;i++)    st[i]+=st[i-1];
        for(i=n-1;i>=0;i--)    sa[--st[x[y[i]]]]=y[i];
        for(swap(x,y),i=p=1,x[sa[0]]=0;i<n;i++)
            x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])?p-1:p++;
    }
    for(i=1;i<n;i++)    rank[sa[i]]=i;
    for(i=k=0;i<n-1;h[rank[i++]]=k)
        for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
void rmq()
{
    int i,j;
    for(i=2;i<n;i++)    Log[i]=Log[i>>1]+1;
    for(i=1;i<n;i++)    dp[i][0]=h[i];
    for(j=1;(1<<j)<n;j++)
        for(i=1;i+(1<<j)-1<n;i++)
            dp[i][j]=min(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
}
int getmin(int a,int b)
{
    int k=Log[b-a+1];
    return min(dp[a][k],dp[b-(1<<k)+1][k]);
}
int main()
{
    scanf("%d",&N);
    int i,j,now=0;
    sm[0]=-1;
    for(i=1;i<=N;i++)
    {
        scanf("%d",&M[i]);
        sm[i]=sm[i-1]+M[i];
        scanf("%d",&v[1]);
        for(j=2;j<=M[i];j++)
        {
            bel[now]=i;
            scanf("%d",&v[j]);
            r[now]=v[j]-v[j-1];
            maxx=max(maxx,r[now]);
            minn=min(minn,r[now++]);
        }
        now++;
    }
    for(i=0;i<sm[N];i++)    r[i]-=minn-1;
    r[sm[N]]=0;
    m=maxx-minn+1;
    for(i=1;i<N;i++)    r[sm[i]]=m+1;
    n=sm[N]+1,m+=2;
    work();
    rmq();
    for(i=1;i<n;i++)
    {
        if(!s[bel[sa[i]]])
        {
            if(sum==N-1)    break;
            sum++;
        }
        s[bel[sa[i]]]++;
    }
    now=1;
    for(;i<n;i++)
    {
        if(!bel[sa[i]])    break;
        s[bel[sa[i]]]++;
        while(s[bel[sa[now]]]>1)    s[bel[sa[now++]]]--;
        ans=max(ans,getmin(now+1,i));
    }
    printf("%d",ans+1);
    return 0;
}
posted @ 2017-01-11 11:37  CQzhangyu  阅读(290)  评论(0编辑  收藏  举报