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

Posted Refun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4698. [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
 
emmmm一道非常难受的后缀数组
一共三步但只想到了前两步(最弱智的一步没有想出来……)
首先把相邻数的所有差值连起来做一个SA ,设不同卡片为不同颜色
然后二分长度,看有没有一个SA区间公共前缀大于该长度并且涵盖所有颜色即为满足
求公共前缀的时候若该后缀的前缀跨越两个颜色是不合法的(在这里被坑了)

#include<iostream>
#include<cstring>
#include<cstdio>
#define MAXN (1000000+10)
using namespace std;
int n,m=2050,t,h,maxn;
int r[MAXN],a[MAXN],Col[MAXN];
int SA[MAXN],Rank[MAXN],Height[MAXN];
int wt[MAXN],wa[MAXN],wb[MAXN];
int stack[MAXN],top;
bool used[MAXN];

bool cmp(int *y,int a,int b,int k)
{
	int arank1=y[a];
	int brank1=y[b];
	int arank2=a+k>=n?-1:y[a+k];
	int brank2=b+k>=n?-1:y[b+k];
	return arank1==brank1 && arank2==brank2;
}

void Build_SA()
{
    int *x=wa,*y=wb;
    for (int i=0;i<m;++i) wt[i]=0;
    for (int i=0;i<n;++i) wt[x[i]=r[i]]++;
    for (int i=1;i<m;++i) wt[i]+=wt[i-1];
    for (int i=n-1;i>=0;--i) SA[--wt[x[i]]]=i; 
    
    for (int j=1;j<=n;j<<=1)
    {
        int p=0;
        for (int i=n-j;i<n;++i) y[p++]=i;
        for (int i=0;i<n;++i) if (SA[i]>=j) y[p++]=SA[i]-j;
    
        for (int i=0;i<m;++i) wt[i]=0;
        for (int i=0;i<n;++i) wt[x[y[i]]]++;
        for (int i=1;i<m;++i) wt[i]+=wt[i-1];
        for (int i=n-1;i>=0;--i) SA[--wt[x[y[i]]]]=y[i];
        
        m=1;swap(x,y);
        x[SA[0]]=0;
        for (int i=1;i<n;++i) 
            x[SA[i]]=cmp(y,SA[i],SA[i-1],j)?m-1:m++;
        if (m>=n) break;
    }
}

void Build_Height()
{
	for (int i=0;i<n;++i) Rank[SA[i]]=i;
	int k=0;
	Height[0]=0;
	for (int i=0;i<n;++i)
	{
		if (!Rank[i]) continue;
		int j=SA[Rank[i]-1];
		if (k) k--;
		while (r[i+k]==r[j+k]) k++;
		Height[Rank[i]]=k;
	}
}

bool check(int len)
{
	int sum=0;
	for (int i=0;i<n;++i) 
	{
		if (Height[i]<len)
		{
			sum=0;
			while (top) 
				used[stack[top--]]=false;
		}
		if (!used[Col[SA[i]]] && Col[SA[i]]==Col[SA[i]+len-1])
		{
			used[Col[SA[i]]]=true;
			sum++;
			stack[++top]=Col[SA[i]];
		}
		if (sum==t) return true;
	}
	return false;
}

int main()
{
	scanf("%d",&t);
	for (int i=1;i<=t;++i)
	{
		scanf("%d",&h);
		maxn=max(maxn,h);
		for (int j=1;j<=h;++j)
		{
			scanf("%d",&a[j]);
			if (j==1) continue;
			r[n++]=a[j]-a[j-1]+510;
			Col[n-1]=i;
		}
	}
	Build_SA();
	Build_Height();
	int l=0,r=105;
	while (l<r)
	{
		int mid=(l+r+1)>>1;
		if (check(mid))
			l=mid;
		else
			r=mid-1;
	}
	printf("%d",l+1);
}

 

以上是关于4698. [SDOI2008]Sandy的卡片后缀数组的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4698 / P2463 [SDOI2008]Sandy的卡片

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

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

[Sdoi2008]Sandy的卡片

[Luogu2463][SDOI2008]Sandy的卡片

[SDOI2008]Sandy的卡片