BZOJ.4516.[SDOI2016]生成魔咒(后缀数组 RMQ)

题目链接

后缀自动机做法见这(超好写啊)

后缀数组是可以做的: 本质不同的字符串的个数为 (子串个数-sum_{ht[i]}),即 (frac{n(n+1)}{2}-sum_{ht[i]}).
如果是每次往后边插入字符,会改变SA[]。但如果向前边插入字符,相当于只加入了一个后缀。
所以离线,把原串反过来。
每次插入一个字符,即新增一个前缀i,它的贡献是(len-max(lcp(pre,i),lcp(i,nxt))),其中(pre,nxt)为与后缀(i)(当前)排名相邻的两个串。
用set维护当前的排名,RMQ求lcp。(懒得写(O(n))的维护单调栈正反跑两遍)

做了几道后缀数组了,竟然真没写过lcp(也许是太简单了)。

(10^5)很容易爆int!

//13228kb	1000ms
#include <set>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
const int N=1e5+5,B=17;

int n,cnt,A[N],ref[N],tm[N],sa[N],sa2[N],rk[N],ht[N],log2[N],st[N][18];
std::set<int> set_rk;
std::set<int>::iterator it;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
int Find(int x)
{
	int l=1,r=cnt,mid;
	while(l<r)
		if(ref[mid=l+r>>1]<x) l=mid+1;
		else r=mid;
	return l;
}
void Get_SA()
{
	int *x=rk,*y=sa2,m=n+1;
	for(int i=0; i<=m; ++i) tm[i]=0;
	for(int i=1; i<=n; ++i) ++tm[x[i]=A[i]];
	for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
	for(int i=n; i; --i) sa[tm[x[i]]--]=i;
	for(int k=1,p=0; k<n; k<<=1,m=p,p=0)
	{
		for(int i=n-k+1; i<=n; ++i) y[++p]=i;
		for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k;

		for(int i=0; i<=m; ++i) tm[i]=0;
		for(int i=1; i<=n; ++i) ++tm[x[i]];
		for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
		for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i];

		std::swap(x,y), x[sa[1]]=p=1;
		for(int i=2; i<=n; ++i)
			x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
		if(p>=n) break;
	}
	for(int i=1; i<=n; ++i) rk[sa[i]]=i;
	ht[1]=0;
	for(int k=0,p,i=1; i<=n; ++i)
	{
		if(rk[i]==1) continue;
		if(k) --k;
		p=sa[rk[i]-1];
		while(i+k<=n && p+k<=n && A[i+k]==A[p+k]) ++k;
		ht[rk[i]]=k;
	}
}
void Init_RMQ()
{
	for(int i=1; i<=n; ++i) st[i][0]=ht[i];
	log2[1]=0;
	for(int i=2; i<=n; ++i) log2[i]=log2[i>>1]+1;
	for(int j=1; j<=log2[n]; ++j)
		for(int i=n-(1<<j-1); i; --i)
			st[i][j]=std::min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
int LCP(int l,int r)
{
	if(l>r) std::swap(l,r);
	++l; int k=log2[r-l+1];
	return std::min(st[l][k],st[r-(1<<k)+1][k]);
}

int main()
{
	n=read();
	for(int i=1; i<=n; ++i) ref[i]=A[i]=read();
	std::sort(ref+1,ref+1+n), cnt=1;
	for(int i=2; i<=n; ++i) if(ref[i]!=ref[i-1]) ref[++cnt]=ref[i];
	for(int i=1; i<=n; ++i) A[i]=Find(A[i]);
//	for(int i=1,lim=n>>1; i<=lim; ++i) std::swap(A[i],A[n-i+1]);//惊了 还写错了== 还是写reverse吧  
	std::reverse(A+1,A+1+n);
	Get_SA(), Init_RMQ();
	long long res=0;
	set_rk.insert(0);
	for(int tmp,i=n; i; --i)//从头开始加入字符(前缀)。
	{
		tmp=0, it=set_rk.upper_bound(rk[i]);
		if(it!=set_rk.end()) tmp=LCP(rk[i],*it);
		if((--it)!=set_rk.begin()) tmp=std::max(tmp,LCP(*it,rk[i]));
		res+=n-i+1-tmp, set_rk.insert(rk[i]);
		printf("%lld
",res);
	}

	return 0;
}

Hash暴力:(60)
那个暴力后缀数组理论也有60啊但是只有20。。懒得看了

#include <set>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define Enter putchar('
')
typedef unsigned long long ull;
const ull seed=131;
const int N=2007;

int n,cnt,A[100005],ref[100005],tm[N],sa[N],sa2[N],rk[N],ht[N];
ull pw[N];
std::set<ull> st;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
int Find(int x)
{
	int l=1,r=cnt,mid;
	while(l<r)
		if(ref[mid=l+r>>1]<x) l=mid+1;
		else r=mid;
	return l;
}
void Get_SA(int n)
{
	int *x=rk,*y=sa2,m=n;//std::min(cnt,n)+2;
	for(int i=0; i<=m; ++i) tm[i]=0;
	for(int i=1; i<=n; ++i) ++tm[x[i]=A[i]];
	for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
	for(int i=n; i; --i) sa[tm[x[i]]--]=i;
	for(int k=1,p=0; k<n; k<<=1,m=p,p=0)
	{
		for(int i=n-k+1; i<=n; ++i) y[++p]=i;
		for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k;

		for(int i=0; i<=m; ++i) tm[i]=0;
		for(int i=1; i<=n; ++i) ++tm[x[i]];
		for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
		for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i];

		std::swap(x,y), x[sa[1]]=p=1;
		for(int i=2; i<=n; ++i)
			x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
		if(p>=n) break;
	}
	for(int i=1; i<=n; ++i) rk[sa[i]]=i;
	ht[1]=0;
	for(int k=0,p,i=1; i<=n; ++i)
	{
		if(rk[i]==1) continue;
		if(k) --k;
		p=sa[rk[i]-1];
		while(i+k<=n && p+k<=n && A[i+k]==A[p+k]) ++k;
		ht[rk[i]]=k;
	}
//	puts("Output:");
//	for(int i=1; i<=n; ++i) printf("SA:No:%d pos:%d
",i,sa[i]);Enter;
//	for(int i=1; i<=n; ++i) printf("RK:%d rank:%d
",i,rk[i]);Enter;
//	for(int i=1; i<=n; ++i) printf("HT:%d ht:%d
",i,ht[i]);Enter;

	int res=n-sa[1]+1;
	for(int i=2; i<=n; ++i) res+=n-sa[i]+1-ht[i];
	printf("%d
",res);
}
void N_2(){
	for(int i=1; i<=n; ++i) Get_SA(i);
}
void HASH(){
	pw[0]=1;
	for(ull i=1; i<=n; ++i) pw[i]=pw[i-1]*i;
	int res=0;
	for(int p=1; p<=n; ++p)
	{
		ull hs=0;
		for(int i=p; i; --i)
		{
			hs=hs*seed+A[i];
			if(!st.count(hs)) ++res,st.insert(hs);
		}
		printf("%d
",res);
	}
}

int main()
{
	freopen("incantation.in","r",stdin);
	freopen("incantation.out","w",stdout);

	n=read();
	for(int i=1; i<=n; ++i) ref[i]=A[i]=read();
	std::sort(ref+1,ref+1+n), cnt=1;
	for(int i=2; i<=n; ++i) if(ref[i]!=ref[i-1]) ref[++cnt]=ref[i];
	for(int i=1; i<=n; ++i) A[i]=Find(A[i]);
	if(n<=2000) {HASH(); return 0;}
	if(n<=2000) {N_2(); return 0;}//mmp
	for(int i=1; i<=n; ++i) printf("%d
",i);
	

	fclose(stdin);fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/SovietPower/p/8709026.html