BZOJ4516 SDOI2016生成魔咒(后缀数组+平衡树)

时间:2023-03-09 09:54:02
BZOJ4516 SDOI2016生成魔咒(后缀数组+平衡树)

  一个字符串本质不同的子串数量显然是总子串数减去所有height值。如果一个个往里加字符的话,每次都会改动所有后缀完全没法做。但发现如果从后往前加的话,每次只会添加一个后缀。于是我们把字符串倒过来,每次往里添加后缀并维护答案。可以用一棵平衡树,每次插入时查询这个名次的前驱后继以更新。

  SA板子敲得磕磕绊绊,没什么救了。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 100010
int n,a[N],b[N],cnt[N],sa[N],sa2[N],tmp[N<<],rk[N<<];
int h[N],s[N],f[N][],lg2[N];
set<int> tree;
int query(int x,int y)
{
if (x>y) swap(x,y);
y--;
return min(f[x][lg2[y-x+]],f[y-(<<lg2[y-x+])+][lg2[y-x+]]);
}
void make(int m)
{
memset(cnt,,sizeof(cnt));
for (int i=;i<=n;i++) cnt[rk[i]=a[i]]++;
for (int i=;i<=m;i++) cnt[i]+=cnt[i-];
for (int i=n;i>=;i--) sa[cnt[rk[i]]--]=i;
for (int k=;k<=n;k<<=)
{
int p=;
for (int i=n-k+;i<=n;i++) sa2[++p]=i;
for (int i=;i<=n;i++) if (sa[i]>k) sa2[++p]=sa[i]-k;
memset(cnt,,sizeof(cnt));
for (int i=;i<=n;i++) cnt[rk[i]]++;
for (int i=;i<=m;i++) cnt[i]+=cnt[i-];
for (int i=n;i>=;i--) sa[cnt[rk[sa2[i]]]--]=sa2[i];
memcpy(tmp,rk,sizeof(rk));
p=;rk[sa[]]=;
for (int i=;i<=n;i++)
{
if (tmp[sa[i]]!=tmp[sa[i-]]||tmp[sa[i]+k]!=tmp[sa[i-]+k]) p++;
rk[sa[i]]=p;
}
if (p>=n) break;
m=p;
}
for (int i=;i<=n;i++)
{
h[i]=max(h[i-]-,);
while (a[i+h[i]]==a[sa[rk[i]-]+h[i]]) h[i]++;
}
for (int i=;i<n;i++) f[i][]=h[sa[i+]];
for (int j=;j<;j++)
for (int i=;i<n;i++)
f[i][j]=min(f[i][j-],f[min(n-,i+(<<j-))][j-]);
lg2[]=;
for (int i=;i<n;i++)
{
lg2[i]=lg2[i-];
if ((<<lg2[i])<=i) lg2[i]++;
}
}
int main()
{
freopen("bzoj4516.in","r",stdin);
freopen("bzoj4516.out","w",stdout);
n=read();
for (int i=;i<=n;i++) b[i]=a[i]=read();
sort(b+,b+n+);
int t=unique(b+,b+n+)-b;
for (int i=;i<=n;i++) a[i]=lower_bound(b+,b+t,a[i])-b;
reverse(a+,a+n+);
make(t);
tree.clear();tree.insert(rk[n]);
long long ans=;cout<<ans<<endl;
for (int i=n-;i>=;i--)
{
set<int>::iterator it=tree.lower_bound(rk[i]);
ans+=n-i+;
if (it==tree.begin()) ans-=query(rk[i],*it);
else if (it==tree.end()) ans-=query(rk[i],*(--it));
else
{
int x=*it,y=*(--it);
ans+=query(x,y);
ans-=query(x,rk[i]),ans-=query(y,rk[i]);
}
tree.insert(rk[i]);
printf("%lld\n",ans);
}
fclose(stdin);fclose(stdout);
return ;
}