【洛谷P4070】生成魔咒

题目

题目链接:https://www.luogu.com.cn/problem/P4070
魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 (1,2) 拼凑起来形成一个魔咒串 ([1,2])
一个魔咒串 (S) 的非空字串被称为魔咒串 (S) 的生成魔咒。
例如 (S=[1,2,1]) 时,它的生成魔咒有 ([1],[2],[1,2],[2,1],[1,2,1]) 五种。(S=[1,1,1]) 时,它的生成魔咒有 ([1],[1,1],[1,1,1]) 三种,最初 S 为空串。
共进行 (n) 次操作,每次操作是在 (S) 的结尾加入一个魔咒字符。每次操作后都需要求出,当前的魔咒串 (S) 共有多少种生成魔咒。

思路

考虑在加入第 (i) 个数字之后会有多少种新的子串生成。显然数量等于 (i) 减有多少个子串之前出现过。
那么我们只需要找到一个 (j(j<i)) 使得 (i)(j) 的 LCS 尽量长即可。假设 LCS 长度为 (l),显然贡献为 (i-l)
那么就把字符串反过来跑一遍 SA,然后从后往前枚举数字,用一个 set 维护之前枚举过的所有数字的 (rank),在 set 上 lower_bound 即可找到两个与 (rank[i]) 最近的位置,用较大的 LCP 去更新即可。
时间复杂度 (O(nlog n))

代码

#include <bits/stdc++.h>
#define AK =
#define IOI 233
using namespace std;
typedef long long ll;

const int N=100010,LG=18,Inf=1e9;
int n,m,s[N],sa[N],x[N],y[N],c[N],rk[N],height[N],lg[N],rmq[N][LG+1];
ll ans,QuantAsk;
map<int,int> b;
set<int> vis;

void SA()
{
	for (int i=1;i<=n;i++) x[i]=s[i],c[x[i]]++;
	for (int i=2;i<=m;i++) c[i]+=c[i-1];
	for (int i=n;i>=1;i--) sa[c[x[i]]--]=i;
	for (int k=1;k<=n;k<<=1)
	{
		int num=0;
		for (int i=n-k+1;i<=n;i++) y[++num]=i;
		for (int i=1;i<=n;i++) if (sa[i]>k) y[++num]=sa[i]-k;
		for (int i=1;i<=m;i++) c[i]=0;
		for (int i=1;i<=n;i++) c[x[i]]++;
		for (int i=2;i<=m;i++) c[i]+=c[i-1];
		for (int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
		swap(x,y);
		x[sa[1]]=num=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]) ? num : ++num;
		m=num;
		if (n==m) break;
	}
}

void geth()
{
	for (int i=1;i<=n;i++) rk[sa[i]]=i;
	for (int i=1,k=0;i<=n;i++)
	{
		if (k) k--;
		int j=sa[rk[i]-1];
		while (s[j+k]==s[i+k]) k++;
		height[rk[i]]=k;
	}
}

void getst()
{
	for (int i=1;i<=n;i++) rmq[i][0]=height[i];
	for (int j=1;j<=LG;j++)
		for (int i=1;i+(1<<j)-1<=n;i++)
			rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);
}

int LCP(int i,int j)
{
	int t=lg[j-i];
	return min(rmq[i+1][t],rmq[j-(1<<t)+1][t]);
}

int main()
{
	scanf("%d",&n);
	for (int i=n;i>=1;i--)
	{
		scanf("%d",&s[i]);
		if (b.find(s[i])==b.end()) b[s[i]]=++m;
		s[i]=b[s[i]];
	}
	SA(); geth(); getst();
	vis.insert(-Inf); vis.insert(Inf);
	lg[1]=0;
	for (int i=2;i<=n;i++)
		lg[i]=lg[i>>1]+1;
	for (int i=n;i>=1;i--)
	{
		ans+=n-i+1;
		int p=*--vis.upper_bound(rk[i]),q=*vis.lower_bound(rk[i]);
		if (p==-Inf && q==Inf) QuantAsk AK IOI;
		if (p!=-Inf && q==Inf) ans-=LCP(p,rk[i]);
		if (p==-Inf && q!=Inf) ans-=LCP(rk[i],q);
		if (p!=-Inf && q!=Inf) ans-=max(LCP(p,rk[i]),LCP(rk[i],q));
		printf("%lld
",ans);
		vis.insert(rk[i]);
	}
	if (QuantAsk AK IOI) return 0;
		else return -1;
}
原文地址:https://www.cnblogs.com/stoorz/p/14050257.html