雅礼集训2017Day7事情的相似度

bitset帮助离线合并实现O(n/w)

然后暴力就可以在n=100000时为所欲为

其实还是有点思想,

从大到小枚举height值,这样每次涉及到的问题的答案一定是height

另外离线会MLE?

那就分两次离呗。

没有什么是一次离线解决不了的,如果有,那就两次。

//从height大到小更新可以简化转移,因为height的最小值一定是你当前枚举的那一隅
#include<cstdio>
#include<cstring>
#include<cctype>
#include<bitset>
#include<algorithm>
#define maxn 100005
#define maxset 60005
using namespace std;

typedef bitset<maxset> Set;

int n,m;
char s[maxn],ss[maxn];
int cnt[maxn],sa[maxn],wa[maxn],wb[maxn],rk[maxn],h[maxn],ans[maxset];

bool sortcmp(const int &a,const int &b){
	return h[a]>h[b];
}

bool cmp(int *r,const int &a,const int &b,int l){
	if( r[a]==r[b] )
		if(a+l<n && b+l<n)
			return r[a+l]==r[b+l];
	return 0;
}

void build(int n,int m){
	int i,j,p,*x=wa,*y=wb,*t;
	for(i=0;i<m;i++) cnt[i]=0;
	for(i=0;i<n;i++) cnt[x[i]=s[i]]++;
	for(i=1;i<m;i++) cnt[i]+=cnt[i-1];
	for(i=n-1;i>=0;i--) sa[--cnt[x[i]]]=i;
	for(p=0,j=1;p<n && j<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<m;i++) cnt[i]=0;
		for(i=0;i<n;i++) cnt[x[y[i]]]++;
		for(i=1;i<m;i++) cnt[i]+=cnt[i-1];
		for(i=n-1;i>=0;i--) sa[--cnt[x[y[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],sa[i-1],j)?p-1:p++;
	}
}

int fa[maxn],seq[maxn];
int find(int now){
	return fa[now]?fa[now]=find(fa[now]):now;
}

void calheight(int n){
	int i,k=0;
	for(i=0;i<n;i++) rk[sa[i]]=i;
	for(i=0;i<n;h[rk[i++]]=k)
		if(rk[i])
			for(k?k--:0;s[i+k]==s[sa[rk[i]-1]+k];k++);
	h[0]=0;
}

Set tak[maxn];
void solve(int Q){
	int u,v;
	for(int i=0;i<n;i++) tak[i]=Set();
	for(int i=0;i<Q;i++){
		scanf("%d%d",&u,&v);
		u=n-u,v=n-v;
		swap(u,v);
		tak[rk[u]][i]=1;
		if(v+1<n) tak[rk[v+1]][i]=1;
	}
	for(int i=1;i<n;i++) tak[rk[i]]^=tak[rk[i-1]];
	memset(fa,0,sizeof fa);
	Set ban;
	for(int i=0;i<n-1;i++){
		u=find(seq[i]),v=find(seq[i]-1);
		Set now=(tak[u] & tak[v]) & (~ban);
		for(int j=now._Find_next(-1);j<Q;j=now._Find_next(j)){
			ban[j]=1;
			ans[j]=h[seq[i]];
		}
		fa[u]=v;
		tak[v]|=tak[u];
	}
	for(int i=0;i<Q;i++)
		printf("%d\n",ans[i]);
}

int main(){
	
	
	scanf("%d%d\n",&n,&m);
	scanf("%s",ss);
	for(int i=0;i<n;i++) s[i]=ss[n-i-1];
	build(n,130);
	calheight(n);
		
	for(int i=0;i<n-1;i++) seq[i]=i+1;
	sort(seq,seq+n-1,sortcmp);

	solve((m>>1));
	solve(m-(m>>1));
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值