【luogu2463&&bzoj4698】sandy的卡片 后缀数组+二分

(http://www.elijahqi.win/2017/07/20/%E3%80%90luogu2463bzoj4698%E3%80%91sandy%E7%9A%84%E5%8D%A1%E7%89%87-%E5%90%8E%E7%BC%80%E6%95%B0%E7%BB%84%E4%BA%8C%E5%88%86/)

题目描述

Sandy和Sue的热衷于收集干脆面中的卡片。

然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片兑换超炫的人物模型。

每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型,首先必须要集够N张卡片,对于这N张卡片,如果他们都有一个相同的子串长度为k,则可以兑换一个等级为k的人物模型。相同的定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串。

Sandy的卡片数远远小于要求的N,于是Sue决定在Sandy的生日将自己的卡片送给Sandy,在Sue的帮助下,Sandy终于集够了N张卡片,但是,Sandy并不清楚他可以兑换到哪个等级的人物模型,现在,请你帮助Sandy和Sue,看看他们最高能够得到哪个等级的人物模型。

输入输出格式

输入格式:

第一行为一个数N,表示可以兑换人物模型最少需要的卡片数,即Sandy现在有的卡片数

第i+1行到第i+N行每行第一个数为第i张卡片序列的长度Mi,之后j+1到j+1+Mi个数,用空格分隔,分别表示序列中的第j个数

输出格式:

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

输入输出样例

输入样例#1:

2
2 1 2
3 4 5 9
输出样例#1:

2
说明

数据范围:

30%的数据保证n<=50

100%的数据保证n<=1000

由于这题没有说mi的范围 所以我用了一个离散化 谨慎一点点,交程序的时候一开始只有10分,后来看到 count数组开小了,count1[N] 然而在程序里我直接调用了count[N+1100] 。。

algorithm库里也有count 所以只能用count1

#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
#define inf 0x7f7f7f7f
#define N 110000
using namespace std;
int a[N],id[N],n,n1,tmp,tmp1,tmp2,minn,a1[N],count1[N+1200],rank[N<<1],rank1[N],tmp3[N],height[N],sa[N],m,k,l,r,ans,answer;
inline int min(int x,int y){
    return x<y?x:y;
} 
inline int max(int x,int y){
    return x>y?x:y;
}
inline bool check(int x){
    bool visit[1100];int cnt=0;
    for (int i=1;i<=n;++i){
        if (height[i]>=x){
            if (!visit[id[sa[i]]]) cnt++,visit[id[sa[i]]]=true;
        }else{
            memset(visit,false,sizeof(visit));
            visit[id[sa[i]]]=true;cnt=1;
        }
        if (cnt==answer) return true;
    }
    return false;
}
map<int,int> mm;
int main(){
    //freopen("2463.in","r",stdin);
    //freopen("2463.out","w",stdout);
    scanf("%d",&n);n1=0;l=1,r=1100; answer=n;
    for (int i=1;i<=n;++i){
        scanf("%d%d",&tmp,&tmp1);tmp-=1;r=min(r,tmp);
        while(tmp--){
             scanf("%d",&tmp2);a[++n1]=tmp2-tmp1;tmp1=tmp2;minn=min(minn,a[n1]);id[n1]=i;
        }
        a[++n1]=inf;id[n1]=i;
    }
    n=n1;
    for (int i=1;i<=n;++i){
        if (a[i]<inf) a[i]-=minn-1,a1[i]=a[i];
        if (a[i]==inf) a[i]=N+id[i],a1[i]=a[i]; 
    }
    sort(a1+1,a1+n+1);
    for (int i=1;i<=n;++i) mm[a1[i]]=i;
    for (int i=1;i<=n;++i) a[i]=mm[a[i]];
    //for (int i=1;i<=n1;++i) printf("%d ",a[i]);printf("\n");
    memset(count1,0,sizeof(count1));
    memset(rank,0,sizeof(rank));
    for (int i=1;i<=n;++i) count1[a[i]]=1;
    for (int i=1;i<=N+1100;++i) count1[i]+=count1[i-1];
    for (int i=1;i<=n;++i) rank[i]=count1[a[i]];
    m=count1[N+1100];k=0;
    for (int p=1;k!=n;p<<=1,m=k){
        for (int i=1;i<=m;++i) count1[i]=0;
        for (int i=1;i<=n;++i) count1[rank[i+p]]++;
        for (int i=1;i<=m;++i) count1[i]+=count1[i-1];
        for (int i=n;i>=1;--i) tmp3[count1[rank[i+p]]--]=i;
        for (int i=1;i<=m;++i) count1[i]=0;
        for (int i=1;i<=n;++i) count1[rank[i]]++;
        for (int i=1;i<=m;++i) count1[i]+=count1[i-1];
        for (int i=n;i>=1;--i) sa[count1[rank[tmp3[i]]]--]=tmp3[i];
        memcpy(rank1,rank,sizeof(rank)>>1);
        rank[sa[1]]=k=1;
        for (int i=2;i<=n;++i){
            if (rank1[sa[i-1]]!=rank1[sa[i]]||rank1[sa[i-1]+p]!=rank1[sa[i]+p]) ++k;
            rank[sa[i]]=k;
        }
    }
    k=0;
    for (int i=1;i<=n;++i){
        if (rank[i]==1) continue;
        k=k==0?0:k-1;
        while (a[i+k]==a[sa[rank[i]-1]+k]) ++k;
        height[rank[i]]=k;
    }
    //for (int i=1;i<=n;++i) printf("%d ",height[i]);
    while (l<=r){
        int mid=(l+r)>>1;
        if (check(mid)) ans=mid,l=mid+1;else r=mid-1;
    }
    printf("%d",ans+1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值