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;
}