字符串

考虑同构的性质

对每个字符维护上一次出现在哪个位置,容易发现对于两个串,如果他们同构,那么这两个串的 pre 序列一定是完全一样的。

我们使用类似 SA 求 lcp 的套路,对这个东西做后缀排序。因为每个后缀的 pre 序列不一样,所以只能使用 sort+二分 Hash 然后复杂度就成了 (O(nlog^3n))

虽然理论复杂度是对的,但奈何这个东西的常数实在是太大大大大大了,一组极限数据,本地运行了 23s。

#include<bits/stdc++.h>
using namespace std;
#define u64 unsigned long long

template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}

const u64 base = 131;
const int np = 5e4 + 5;
const int cp = 2 * np + np * 17;
int n,Cnt;
u64 p[np];
int a[np],ver[np],sa[np];
int bac[np];
struct Node{
	Node *ls,*rs;
	u64 Val;
}mem[cp],*pool = mem,*rot[np];
inline Node *New(){
	++pool;
	pool->ls = pool->rs=0;
	pool->Val = 0;
	return pool;
}
inline Node *build(int L,int R)
{
	Node *u = New();
	if(L == R){
		u->ls = u->rs = 0;
		u->Val = 0;
	}
	else{
		int mid = L + R >> 1;
		u->ls = build(L,mid);
		u->rs = build(mid+1,R);
	}
	return u;
}
inline void upd(Node *u,Node *pr,int l,int r,int pos,int x)
{
	*u = *pr;
	if(l == r){
		u->Val = x;
		return;
	}
	int mid = l + r >> 1;
	if(pos <= mid) upd(u->ls=New(),pr->ls,l,mid,pos,x);
	else upd(u->rs=New(),pr->rs,mid+1,r,pos,x);
	u->Val = (u->ls->Val * p[r-mid]) + u->rs->Val;
}
inline u64 query(Node *u,int l,int r,int L,int R)
{
	if(L <= l && r <= R){
		return u->Val;
	}
	if(r < L || R < l) return 0;
	int mid = l + r >> 1;
	if(R <= mid) return query(u->ls,l,mid,L,R);
	else if(mid+1 <= L) return query(u->rs,mid+1,r,L,R);
	else return query(u->ls,l,mid,L,mid) * p[R-mid] + query(u->rs,mid+1,r,mid+1,R);
}
inline int Lcp(int u,int v)
{
	if(u == v) return n-u+1;
	if(v < u) swap (u,v);
	int l=0,r = n-v,ans(-1);
	while(l <= r)
	{
		int mid = l + r >> 1;
		if(query(rot[u],1,n,u,u+mid) == query(rot[v],1,n,v,v+mid)){
			l = mid + 1;
			ans = mid;
		}
		else r = mid - 1;
	}
	return ans+1;
}
inline int Get_(Node *u,int pos)
{
	if(pos > n) return -1;
	else return query(u,1,n,pos,pos);
}
inline bool cmp(int u,int v)
{
	int len = Lcp(u,v);
	return Get_(rot[u],u+len) < Get_(rot[v],v+len);
}
inline void Clr()
{
	for(int i=1;i <= n;i ++) bac[i] = 0,rot[i] = 0;
	pool = mem;
}

signed  main()
{
	int V = 5e4;
	p[0] = 1;
	for(int i=1;i <= V; i ++) p[i] = p[i-1] * base;
	while(Clr(),scanf("%d",&n)!=EOF)
	{
		for(int i=1;i <= n;i ++) read(a[i]);
		rot[n] = build(1,n);
		bac[a[n]] = n;
		for(int i=n-1;i >= 1;i --)
		{
			if(bac[a[i]]) upd(rot[i]=New(),rot[i + 1],1,n,bac[a[i]],bac[a[i]]-i);
			else rot[i] = rot[i + 1];
			bac[a[i]]=i;
		}
		for(int i=1;i <= n;i ++) sa[i] = i;
		sort(sa + 1,sa + 1 + n,cmp);	
		u64 Ans = 1ull * n * (n + 1)/2;
		for(int i=2;i <= n;i ++) Ans -= Lcp(sa[i],sa[i-1]);
		printf("%llu
",Ans);
	}
 }

贴上这份只能得 (60pts) 的代码

原文地址:https://www.cnblogs.com/-Iris-/p/15495865.html