AtCoder Beginner Contest 213 F题 题解

F - Common Prefixes

该题也是囤了好久的题目了,看题目公共前缀,再扫一眼题目,嗯求每个后缀与其他后缀的公共前缀的和,那不就是后缀数组吗?对于这类问题后缀数组可是相当在行的。
我们用后缀数组的思想转化下题意就是:
经过后缀排序,我们得到\(height\)数组,考虑排名从小到大依次处理每一个字符串,显然对于一个后缀i而言\(ans(sa[i])=\sum_{j=2}^{i} min_{k=j}^i (a_k)+\sum_{j=i+1}^{n} min_{k=i+1}^j(a_k)\)这样我们可以考虑拆开考虑,只考虑起一个\(\sum\)的结果,可以发现如果我们有办法做第一个的话,第二个只需要到这循环做一遍就可以了。接下来我们只考虑第一个\(\sum\)如何快速计算,一般这种题都有两种思路要么是根据某种数据结构达到快速查询到所需结果的目的,要么是根据i和i-1的关系来从上一个值递推出这个值得结果。第一种办法显然有点想不通,考虑第二种结果,我们将i从1递增看看到底有什么关系,
\(i=1\) 答案为空集
\(i=2\) \(ans=a_2\)
\(i=3\) \(ans=min(a_2,a_3)+a_3\)
\(i=4\) \(ans=min(a_2,a_3,a_4)+min(a_3,a_4)+a_4\)
\(...\)
大致可以得到些结论,当i从2开始递增时,每次都会新增一个数\(a_i\)并且对当前所有的数都和\(a_i\)取min再操作。也就是说,我们每次都要和新来的数比大小,看当前保存的数能否比他小,若新来的数比较小的话就取代之前的数,这不就是单调栈吗?我们可以维护一个递增的单调栈,并且维护下每个数出现的次数,若有元素出站,就将它的次数累加到将它弹出站的元素身上。

查看代码

//不等,不问,不犹豫,不回头.
#include
#define _ 0
#define ls p<<1
#define db double
#define rs p<<1|1
#define P 1000000007
#define ll long long
#define INF 1000000000
#define get(x) x=read()
#define PLI pair
#define PII pair
#define ull unsigned long long
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(x,y,z) for(int x=y;x<=z;++x)
#define fep(x,y,z) for(int x=y;x>=z;--x)
#define go(x) for(RE int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
using namespace std;
const int N=2e6+10;
int n,tax[N],rak[N],sa[N],tp[N],height[N],M;
int sack[N],top,cnt[N];
ll ans[N];
char c[N];

inline int read()
{
int x=0,ff=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*ff;
}

inline void Qsort()
{
rep(i,0,M) tax[i]=0;
rep(i,1,n) tax[rak[i]]++;
rep(i,1,M) tax[i]+=tax[i-1];
fep(i,n,1) sa[tax[rak[tp[i]]]--]=tp[i];
}

inline void Suffixsort()
{
M=75;
rep(i,1,n) rak[i]=c[i]-'0'+1,tp[i]=i;
Qsort();
for(int w=1,p=0;p<n;M=p,w<<=1)
{
p=0;
rep(i,1,w) tp[++p]=n-w+i;
rep(i,1,n) if(sa[i]>w) tp[++p]=sa[i]-w;
Qsort();
swap(tp,rak);
rak[sa[1]]=p=1;
rep(i,2,n) rak[sa[i]]=(tp[sa[i]]tp[sa[i-1]]&&tp[sa[i]+w]tp[sa[i-1]+w])?p:++p;
}
}

inline void get_height()
{
int j,k=0;
rep(i,1,n)
{
if(k) k--;
j=sa[rak[i]-1];
while(c[i+k]==c[j+k]) ++k;
height[rak[i]]=k;
}
}

inline void solve()
{
rep(i,1,n) ans[i]+=n-i+1;
ll p=0;
rep(i,2,n)
{
int ct=1;
while(top&&height[i]<=height[sack[top]])
{
p-=(ll)height[sack[top]]cnt[top];
ct+=cnt[top];
top--;
}
sack[++top]=i;cnt[top]=ct;
p+=(ll)height[sack[top]]
cnt[top];
ans[sa[i]]+=p;
}
top=0;p=0;
fep(i,n-1,1)
{
int ct=1;
while(top&&height[i+1]<=height[sack[top]])
{
p-=(ll)height[sack[top]]cnt[top];
ct+=cnt[top];
top--;
}
sack[++top]=i+1;cnt[top]=ct;
p+=(ll)height[sack[top]]
cnt[top];
ans[sa[i]]+=p;
}
rep(i,1,n) putl(ans[i]);
}

int main()
{
//freopen("1.in","r",stdin);
get(n);
scanf("%s",c+1);
Suffixsort();
get_height();
solve();
return (0_0);
}
//以吾之血,铸吾最后的亡魂.


posted @ 2021-08-11 21:28  逆天峰  阅读(146)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//