1. 程式人生 > >4698. [SDOI2008]Sandy的卡片【後綴數組】

4698. [SDOI2008]Sandy的卡片【後綴數組】

pac 第一個 ack oid n) names sdoi2008 %d bsp

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的卡片【後綴數組】