zoukankan      html  css  js  c++  java
  • 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();
        }
        
    }
  • 相关阅读:
    [翻译] REMenu
    图片切换的效果
    刮奖效果
    粒子雪花效果
    Shimmer辉光动画效果
    解决Bootstrap 标签页(Tab)插件切换echarts不显示问题
    主要是解决,作为一个数据共享的数据库,存在的数据库统计,然后将计算的数据量输出到自己使用的数据库,进行主页面展示。
    使用kettle来根据时间戳或者批次号来批量导入数据,达到增量的效果。
    使用java的Calendar工具类获取到本月的第一天起始时间和最后一天结束时间。
    mysql根据分组和条件查询以后如何统计记录的条数
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/14037568.html
Copyright © 2011-2022 走看看