2572. 生成魔咒

题目链接

2572. 生成魔咒

魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。
例如可以将魔咒字符 \(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\) 共有多少种生成魔咒。

输入格式

第一行一个整数 \(n\)
第二行 \(n\) 个数,第 \(i\) 个数表示第 \(i\) 次操作加入的魔咒字符。

输出格式

输出 \(n\) 行,每行一个数。第 \(i\) 行的数表示第 \(i\) 次操作后 \(S\) 的生成魔咒数量。

数据范围

\( 1 \leq n \leq 10^{5} \text {, } \)
用来表示魔咒字符的数字 \(x\) 满足 \(1≤x≤10^9\)

输入样例:

7
1 2 3 3 3 1 2

输出样例:

1
3
6
9
12
17
22

解题思路

后缀数组

先考虑静态求不同子串的问题,即给定一个字符串,求其不同子串的数量。所有子串构成的集合等于所有后缀的所有前缀构成的集合。后缀数组中 \(height[i]\) 中定义的是排名为 \(i\) 的后缀与排名为 \(i-1\) 的后缀的最长公共前缀(不包含替补字符),按排名遍历后缀,当遍历到排名为 \(i\) 的后缀时,其含有的前缀数量为 \(n-sa[i]+1\),而又与排名为 \(i-1\) 的后缀含有的公共前缀数量为 \(height[i]\),为了避免重复,排名为 \(i\) 的后缀对整体的贡献为 \(n-sa[i]+1-height[i]\),故所有不同子串数量为 \(\sum_{i=1}^n n-sa[i]+1-height[i]\)
现在考虑每增加一个字符不同子串的数量。因为增加一个字符对前面的后缀有影响,不妨反过来,即将整个字符串翻转,每次删除第一个字符(对应从后往前操作)求不同子串数量,这时每次都只会删除一个后缀,同时用双链表维护排名后缀之间的连接关系,每删除一个后缀,答案减去该后缀对答案的贡献,同时加上新的连接对答案的贡献。另外,由于字符过大且答案与值的大小无关,所以可用哈希表离散化

  • 时间复杂度:\(O(nlogn)\)

代码

// Problem: 生成魔咒
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2574/
// Memory Limit: 64 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=1e5+5;
int n,m,sa[N],height[N],rk[N],c[N],x[N],y[N],cnt;
unordered_map<int,int> mp;
int idx,s[N];
LL res[N];
int down[N],up[N];
int get(int x)
{
	if(mp.count(x))return mp[x];
	return mp[x]=++idx;
}
void get_sa()
{
	for(int i=1;i<=n;i++)c[x[i]=s[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)
	{
		cnt=0;
		for(int i=n-k+1;i<=n;i++)y[++cnt]=i;
		for(int i=1;i<=n;i++)
			if(sa[i]>k)y[++cnt]=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]]=1,cnt=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])?cnt:++cnt;
		if(cnt==n)break;
		m=cnt;
	}
}
void get_height()
{
	for(int i=1;i<=n;i++)rk[sa[i]]=i;
	for(int i=1,k=0;i<=n;i++)
	{
		if(rk[i]==1)continue;
		if(k)k--;
		int j=sa[rk[i]-1];
		while(i+k<=n&&s[i+k]==s[j+k])k++;
		height[rk[i]]=k;
	}	
}

int main()
{
    scanf("%d",&n);
    for(int i=n;i>=1;i--)scanf("%d",&s[i]),s[i]=get(s[i]);
    m=n;
    get_sa();
    get_height();
    LL t=0;
    down[0]=1,up[n+1]=n;
    for(int i=1;i<=n;i++)
    {
    	t+=n-sa[i]+1-height[i];
    	down[i]=i+1,up[i]=i-1;
    }
    for(int i=1;i<=n;i++)
    {
    	res[i]=t;
    	int j=rk[i],k=down[j];
    	t-=n-sa[j]+1-height[j];
    	t-=n-sa[k]+1-height[k];
    	height[k]=min(height[k],height[j]);
    	t+=n-sa[k]+1-height[k];
    	up[k]=up[j];
    	down[up[j]]=k;
    }
    for(int i=n;i>=1;i--)printf("%lld\n",res[i]);
    return 0;
}
posted @ 2022-05-04 21:01  zyy2001  阅读(55)  评论(0编辑  收藏  举报