Bzoj 4698: Sdoi2008 Sandy的卡片
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 13 Solved: 7
[ 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
Output
一个数k,表示可以获得的最高等级。
Sample Input
2
2 1 2
3 4 5 9
2 1 2
3 4 5 9
Sample Output
2
Analysis
标解据说是用kmp,复杂度O(NM^2),我们用后缀数组复杂度能压到(NlogN)
这道题的最可恶的地方在于没给出“卡片上的数字”的数据规模,我只好假设是在int范围内
我只会nlogn的后缀数组,因此要用基数排序,也就要用离散
先把所有的数字读入到一个数组中,中间用互不相同的间隔符隔开
然后初始化后缀数组
然后二分k,即串的最长长度,check()就是进行一遍O(N)的扫描,用k分组然后看有没有一组里面包含了来自所有原串的子串(这里我用了一个栈,可以保证均摊时间复杂度是O(N)的)
完
Code
//Sdoi2008 Sandy的卡片 后缀数组+分组+二分
#include <cstdio>
#include <cstring>
#include <map>
#define inf (long long)0x7fffffff
#define maxn 1000100
#define ll long long
using namespace std;
ll sa[maxn], rank[maxn], wa[maxn], wb[maxn], ws[maxn], wv[maxn],
height[maxn], n, card[maxn], hash[maxn], s[maxn], top,
N, M;
bool vis[maxn];
map<ll,ll> m;
ll read(ll &x)
{
char c;
for(c=getchar();c<'0' or c>'9';c=getchar());
for(x=0;c>='0' and c<='9';c=getchar())x=x*10+c-48;
return x;
}
void input()
{
ll i, j;
read(N);
for(i=1;i<=N;i++)
{
read(M);
for(j=0;j<M;j++)read(card[n+j]),hash[n+j]=i;
card[n+M]=inf+i;
n+=M+1;
}
for(i=n-1;i;i--)card[i]=card[i]-card[i-1];
}
bool cmp(ll *r, ll a, ll b, ll l)
{return r[a]==r[b] and r[a+l]==r[b+l];}
void init(ll *r, ll *sa, ll n, ll m)
{
ll i, j, k=0, p, *x=wa, *y=wb, *t;
for(i=0;i<m;i++)ws[i]=0;
for(i=0;i<n;i++)ws[x[i]=r[i]]++;
for(i=1;i<m;i++)ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--)sa[--ws[x[i]]]=i;
for(j=1,p=1;p<n;j<<=1,m=p)
{
for(p=0,i=n-j;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++)ws[i]=0;
for(i=0;i<n;i++)ws[wv[i]]++;
for(i=1;i<m;i++)ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--)sa[--ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,i=1,p=1,x[sa[0]]=0;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
for(i=1;i<n;i++)rank[sa[i]]=i;
for(i=0;i<n-1;height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
void lisan()
{
ll cnt=0, i;
for(i=0;i<n;i++)if(m.find(card[i])==m.end())m[card[i]]=++cnt;
for(i=0;i<n;i++)card[i]=m[card[i]],M=max(M,card[i]);
}
bool check(const ll k)
{
ll i;
for(;top;vis[s[top--]]=false);
for(i=1;i<=n;i++)
{
if(height[i]>=k)
{
if(top==0)
{
vis[hash[sa[i-1]]]=true;
s[++top]=hash[sa[i-1]];
}
if(!vis[hash[sa[i]]])
{
vis[hash[sa[i]]]=true;
s[++top]=hash[sa[i]];
}
}
else
{
if(top==N)return true;
for(;top;vis[s[top--]]=false);
}
}
return false;
}
ll work()
{
ll l, r, mid;
l=1, r=n, mid=(l+r+1)>>1;
while(l<r)
{
if(check(mid))l=mid;
else r=mid-1;
mid=(l+r+1)>>1;
}
return l;
}
int main()
{
input();
lisan();
init(card,sa,n+1,M+1);
printf("%lld\n",work()+1);
return 0;
}