ICPC2018焦作站 H. Can You Solve the Harder Problem?

题意:

给出一个数字串,问所有本质不同的子串的最大值之和

如果没有本质不同的要求,就是用单调栈求出每个数字前后第一个大于它的位置,扫一遍计算即可

现在要本质不同,用后缀数组

按字典序依次计算每个后缀的贡献

对于已经按字典序从小到大排好序的后缀i-1和i来说

以i为子串左端点,[i,height[i]]之间的为子串右端点 的子串已经在以i-1位左端点的字串中统计过了

所以对于每一个后缀i,新增的是以i为左端点,以[i+height[i],n]为右端点的子串的答案

每个后缀的答案分为两部分

设区间[i,i+height[i]-1]之间最大的是第j个数,第j个数后面第一个更大的是第k个数

第一部分为以i为左端点,以[k,n]之间的为右端点的答案

它的答案等同于以k为左端点,以[k,n]之间的为右端点的答案

这可以提前对每一个k都求出来

求法:

假设第i个数后面第一个更大的是第nxt个数,那么以i为左端点的贡献就是(nxt-i)* 第i个数

从右往左扫一遍,可以求出i为左端点,以[i,n]之间的为右端点的答案

第二部分为以i为左端点,以[i+height[i],k-1]为右端点的子串答案

这一共有k-1-(i+height[i])个区间,他们的最大值都是第j个数

找这个j的方法有很多

可以根据nxt倍增

求nxt可以用单调栈

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

#define N 200002
#define M 1000002

typedef long long LL;

int n,k,mx,a[N],v[M],p,q,sa[2][N],rk[2][N],h[N];

int st[N],top;
int nxt[N][20],qp,bit[20];
LL sum[N];

void mul(int *sa,int *rk,int *SA,int *RK)
{
    for(int i=1;i<=n;i++) v[rk[sa[i]]]=i;
    for(int i=n;i;i--)
        if(sa[i]>k) 
            SA[v[rk[sa[i]-k]]--]=sa[i]-k;
    for(int i=n-k+1;i<=n;i++)
        SA[v[rk[i]]--]=i;
    for(int i=1;i<=n;i++)
        RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+k]!=rk[SA[i-1]+k]);
}

void presa()
{
    p=0;
    q=1;
    qp=0;
    for(int i=0;i<=mx;++i) v[i]=0;
    for(int i=1;i<=n;i++) v[a[i]]++;
    for(int i=1;i<=mx;i++) v[i]+=v[i-1];
    for(int i=1;i<=n;i++) 
        sa[p][v[a[i]]--]=i;
    for(int i=1;i<=n;i++) 
        rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(a[sa[p][i-1]]!=a[sa[p][i]]);
    for(k=1;k<n;k<<=1,swap(p,q),++qp)
        mul(sa[p],rk[p],sa[q],rk[q]);
    for(int i=1,kk=0;i<=n;i++)
    {
        int j=sa[p][rk[p][i]-1]; 
        while(a[i+kk]==a[j+kk]) kk++;
        h[rk[p][i]]=kk;
        if(kk) kk--; 
    }
}

void solve()
{
    st[top=1]=n+1;
    for(int i=n;i;--i)
    {
        while(top && a[i]>=a[st[top]]) top--;
        nxt[i][0]=st[top];
        st[++top]=i;
    }
    nxt[n+1][0]=n+1;
    for(int i=1;i<=qp;++i)
        for(int j=1;j<=n+1;++j)
            nxt[j][i]=nxt[nxt[j][i-1]][i-1];
    for(int i=1;i<=n;++i) sum[i]=1ll*(nxt[i][0]-i)*a[i];
    for(int i=n;i;--i)
        if(nxt[i][0]<=n) sum[i]+=sum[nxt[i][0]];
    LL ans=sum[sa[p][1]];
    int s,pos,now,to;
    for(int i=2;i<=n;++i)
    {
        s=sa[p][i]+h[i];
        now=sa[p][i];
        for(int j=qp;j>=0;--j)
            if(nxt[now][j]<s) now=nxt[now][j];
        to=nxt[now][0];
        if(to<=n) ans+=sum[to];
        ans+=1ll*(to-s)*a[now];
    }
    printf("%lld
",ans); 
}

int main()
{
    int T;
    scanf("%d",&T);
    bit[0]=1;
    for(int i=1;i<=19;++i) bit[i]=bit[i-1]<<1;
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i) 
        {
            scanf("%d",&a[i]);
            mx=max(mx,a[i]);
        }
        a[n+1]=2e6;
        presa();
        //for(int i=1;i<=n;i++) printf("%d ",sa[p][i]);puts("");
        //for(int i=2;i<=n;i++) printf("%d ",h[i]);
        solve();
    }
    
}
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/14037568.html