【SDOI2008】Sandy的卡片
SA 经典题型:后缀数组+二分答案。
本题不难,我们需要先要处理题目重新定义的这个“相等”。这个处理方式和一道 HDU 的题很像,好像叫 Musical Theme。我们只需要记录数组相邻两个数字的变化量即可。因为在数字加上同一个数后,差是不变的。
我们把每个处理后的数组拼接到一起,求一遍后缀数组,我们需要的是二分答案 \(x\),只要有连续的 height
大于 \(x\),且每个字符串都出现过,就返回 true
。
有个细节需要注意一下的。传统的两个字符串拼接中间要加一个 $
,放置两个串互相影响。而这里是 \(n\) 个字符串拼接,我们为了区别,我们需要加 \(1\) 个 $
,\(2\) 个,\(3\) 个……,以此类推,防止 1 0 -1 $ 1 -1 $ 1
这种情况的出现。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int MAXN=1e6+10;
int n,m,len;
int sa[MAXN],rnk[MAXN],height[MAXN],w[101],num[MAXN],s[MAXN],bel[MAXN];
struct pr {
int x,y,id;
bool operator <(const pr b)const {
if(x!=b.x) {
return x<b.x;
}
return y<b.y;
}
}p[MAXN];
void get_sa() {
for(int i=1;i<=len;i++) {
rnk[i]=s[i];
}
for(int l=1;l<len;l<<=1) {
for(int i=1;i<=len;i++) {
p[i].x=rnk[i];
p[i].y=(i+l<=len? rnk[i+l]:0);
p[i].id=i;
}
sort(p+1,p+len+1);
int t=0;
for(int i=1;i<=len;i++) {
if(p[i].x!=p[i-1].x||p[i].y!=p[i-1].y) {
t++;
}
rnk[p[i].id]=t;
}
}
for(int i=1;i<=len;i++) {
sa[rnk[i]]=i;
}
}
void get_height() {
int h=0;
for(int i=1;i<=len;i++) {
if(h) {
h--;
}
if(rnk[i]==1) {
continue;
}
int p=i+h;
int q=sa[rnk[i]-1]+h;
while(p<=len&&q<=len&&s[p]==s[q]) {
p++;
q++;
h++;
}
height[rnk[i]]=h;
}
}
bool check(int x) {
set<int> s;
s.insert(bel[sa[1]]);
for(int i=2;i<=len;i++) {
if(height[i]>=x) {
s.insert(bel[sa[i]]);
}
else {
s.clear();
s.insert(bel[sa[i]]);
}
if(s.size()==n) {
return 1;
}
}
return 0;
}
signed main() {
int l=0,r=1e9+10;
cin>>n;
for(int i=1;i<=n;i++) {
m=read();
r=min(r,m);
for(int j=1;j<=m;j++) {
w[j]=read();
if(j>1) {
s[++len]=w[j]-w[j-1];
bel[len]=i;
}
}
if(i!=n) {
for(int j=1;j<=i;j++) {
s[++len]=1e9;
}
}
}
get_sa();
get_height();
while(l+1<r) {
int mid=(l+r)>>1;
if(check(mid)) {
l=mid;
}
else {
r=mid;
}
}
if(check(r)) {
cout<<r+1<<endl;
}
else {
cout<<l+1<<endl;
}
return 0;
}