BZOJ 4698: Sdoi2008 Sandy的卡片

Posted NeighThorn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 4698: Sdoi2008 Sandy的卡片相关的知识,希望对你有一定的参考价值。

4698: Sdoi2008 Sandy的卡片

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 225  Solved: 95
[Submit][Status][Discuss]

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

HINT

Source

分析:

深夜写模板题...听起来不错...

把$n$个串连起来求一个后缀数组,然后二分答案$k$,判断是否存在一个连续的$height>=k$区间出现了$n$个串...

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=1000+5,maxm=1000+5,maxlen=2000000+5;

int n,m,Min,s[maxlen],id[maxlen],len[maxn],vis[maxn],str[maxn][maxm];
int sa[maxlen],gs[maxlen],wb[maxlen],wv[maxlen],ran[maxlen],height[maxlen];

inline bool cmp(int *x,int a,int b,int l){
	return x[a]==x[b]&&x[a+l]==x[b+l];
}

inline void da(int *sa,int *x,int n,int m){
	int i,j,p,*y=wb;
	for(i=0;i<m;i++) gs[i]=0;
	for(i=0;i<n;i++) gs[x[i]]++;
	for(i=1;i<m;i++) gs[i]+=gs[i-1];
	for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
	for(j=1,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<n;i++) wv[i]=x[y[i]];
		for(i=0;i<m;i++) gs[i]=0;
		for(i=0;i<n;i++) gs[wv[i]]++;
		for(i=1;i<m;i++) gs[i]+=gs[i-1];
		for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
		p=1;swap(x,y);x[sa[0]]=0;
		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}

inline void calcheight(int n){
	int i,j,k=0;
	for(i=0;i<=n;i++) ran[sa[i]]=i;
	for(i=0;i<n;height[ran[i++]]=k)
		for(k?k--:233,j=sa[ran[i]-1];s[j+k]==s[i+k];k++);
}

inline bool check(int k){
	int cnt=0;
	for(int l=1,r;l<=n;l=r+1){
		while(l<n&&height[l]<k) l++;
		r=l;if(r==n&&height[r]<k) return false;
		while(r<n&&height[r+1]>=k) r++;
		if(r-l+2>=m){
			memset(vis,0,sizeof(vis));cnt=0;
			for(int i=l-1;i<=r;i++)
				if(id[sa[i]]) cnt+=vis[id[sa[i]]]^1,vis[id[sa[i]]]=1;	
			if(cnt==m) return true;
		}
	}
	return false;
}

signed main(void){
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
#endif
	scanf("%d",&m);Min=1000;
	for(int i=1;i<=m;i++){
		scanf("%d",&len[i]),Min=min(Min,len[i]);
		for(int j=1;j<=len[i];j++)
			scanf("%d",&str[i][j]);
	}
	n=0;
	for(int i=1;i<=m;i++,ran[n]=s[n++]=2000+i)
		for(int j=2;j<=len[i];j++)
			id[n]=i,ran[n]=s[n++]=str[i][j]-str[i][j-1];
	da(sa,ran,n+1,4000);calcheight(n);
	int l=1,r=Min-1,ans=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid)) ans=mid,l=mid+1;
		else r=mid-1;
	}
	printf("%d\n",ans+1);
	return 0;
}

  


By NeighThorn

 

以上是关于BZOJ 4698: Sdoi2008 Sandy的卡片的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4698 / P2463 [SDOI2008]Sandy的卡片

BZOJ4698 & 洛谷2463:[SDOI2008]Sandy的卡片——题解

4698. [SDOI2008]Sandy的卡片后缀数组

[Luogu2463][SDOI2008]Sandy的卡片

Bzoj2037 [Sdoi2008]Sue的小球

[Sdoi2008]Sandy的卡片