【SDOI2008】Sandy的卡片(二分答案+后缀数组)

【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;
}

posted @ 2021-02-14 19:48  huayucaiji  阅读(61)  评论(0编辑  收藏  举报