zoukankan      html  css  js  c++  java
  • 【NOI2015】品酒大会[后缀数组]

    #131. 【NOI2015】品酒大会

     统计

    一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。

    在大会的晚餐上,调酒师 Rainbow 调制了 nn 杯鸡尾酒。这 nn 杯鸡尾酒排成一行,其中第 ii 杯酒 (1in1≤i≤n) 被贴上了一个标签 sisi,每个标签都是 2626 个小写英文字母之一。设 Str(l,r)Str(l,r) 表示第 ll 杯酒到第 rr 杯酒的 rl+1r−l+1 个标签顺次连接构成的字符串。若 Str(p,po)=Str(q,qo)Str(p,po)=Str(q,qo),其中 1ppon1≤p≤po≤n,1qqon1≤q≤qo≤n,pqp≠q,pop+1=qoq+1=rpo−p+1=qo−q+1=r,则称第 pp 杯酒与第 qq 杯酒是“rr相似” 的。当然两杯“rr相似” (r>1r>1)的酒同时也是“11 相似”、“22 相似”、…、“(r1)(r−1) 相似”的。特别地,对于任意的 1p,qn1≤p,q≤n,pqp≠q,第 pp 杯酒和第 qq 杯酒都是“00相似”的。

    在品尝环节上,品酒师 Freda 轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中第 ii 杯酒 (1in1≤i≤n) 的美味度为 aiai。现在 Rainbow 公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点,如果把第 pp 杯酒与第 qq 杯酒调兑在一起,将得到一杯美味度为 apaqapaq 的酒。现在请各位品酒师分别对于 r=0,1,2,,n1r=0,1,2,…,n−1,统计出有多少种方法可以选出 22 杯“rr相似”的酒,并回答选择 22 杯“rr相似”的酒调兑可以得到的美味度的最大值。

    输入格式

    输入文件的第 11 行包含 11 个正整数 nn,表示鸡尾酒的杯数。

    第 22 行包含一个长度为 nn 的字符串 SS,其中第 ii 个字符表示第 ii 杯酒的标签。

    第 33 行包含 nn 个整数,相邻整数之间用单个空格隔开,其中第 ii 个整数表示第 ii 杯酒的美味度 aiai。

    输出格式

    输出文件包括 nn 行。第 ii 行输出 22 个整数,中间用单个空格隔开。第 11 个整数表示选出两杯“(i1)(i−1)相似”的酒的方案数,第 22 个整数表示选出两杯“(i1)(i−1)相似”的酒调兑可以得到的最大美味度。若不存在两杯“(i1)(i−1)相似”的酒,这两个数均为 00。

    样例一

    input

    10
    ponoiiipoi
    2 1 4 7 4 8 3 6 4 7
    
    

    output

    45 56
    10 56
    3 32
    0 0
    0 0
    0 0
    0 0
    0 0
    0 0
    0 0
    
    

    explanation

    用二元组 (p,q)(p,q) 表示第 pp 杯酒与第 qq 杯酒。

    00 相似:所有 4545 对二元组都是 00 相似的,美味度最大的是 8×7=568×7=56。

    11 相似:(1,8)(1,8) (2,4)(2,4) (2,9)(2,9) (4,9)(4,9) (5,6)(5,6) (5,7)(5,7) (5,10)(5,10) (6,7)(6,7) (6,10)(6,10) (7,10)(7,10),最大的 8×7=568×7=56。

    22 相似:(1,8)(1,8) (4,9)(4,9) (5,6)(5,6),最大的 4×8=324×8=32。

    没有 3,4,5,,93,4,5,…,9 相似的两杯酒,故均输出 00。

    样例二

    input

    12
    abaabaabaaba
    1 -2 3 -4 5 -6 7 -8 9 -10 11 -12
    
    

    output

    66 120
    34 120
    15 55
    12 40
    9 27
    7 16
    5 7
    3 -4
    2 -4
    1 -4
    0 0
    0 0
    
    

    样例三

    见样例数据下载。

    限制与约定

    测试点编号nn 的规模aiai 的规模备注
    1 n=100n=100 ai10000∣ai∣≤10000  
    2 n=200n=200
    3 n=500n=500
    4 n=750n=750
    5 n=1000n=1000 ai1000000000∣ai∣≤1000000000
    6
    7 n=2000n=2000
    8
    9 n=99991n=99991 ai1000000000∣ai∣≤1000000000 不存在“1010相似”的酒
    10
    11 n=100000n=100000 ai1000000∣ai∣≤1000000 所有 aiai 的值都相等
    12 n=200000n=200000
    13 n=300000n=300000
    14
    15 n=100000n=100000 ai1000000000∣ai∣≤1000000000  
    16
    17 n=200000n=200000
    18
    19 n=300000n=300000
    20

    时间限制:1s1s

    空间限制:512MB512MB

    下载

    样例数据下载

    [分析]:

    求 Height 数组,然后按从大到小的顺序排序,因为可以发现 Height 中的大值不会影响小值对答案的贡献。

    每次更新答案,将当前两个字符串合并,即用并查集维护一下。

    他们对于方案数的贡献就是这两个后缀所在的集合个数的乘积,同时维护一下最大最小值就行了。

    时间复杂度 O( nlog2n )

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=3e5+5;
    typedef long long ll;
    int n,h[N],c[N],sa[N],tsa[N],Rank[N],tRank[N];
    int a[N],fa[N],siz[N],nxt[N],mx[N],mn[N];
    ll cnt[N],ans[N];
    char s[N];
    void DA(int maxx=256){
        int p;
        for(int i=0;i<=maxx;i++) c[i]=0;
        for(int i=1;i<=n;i++) c[Rank[i]=s[i]]++;
        for(int i=2;i<=maxx;i++) c[i]+=c[i-1];
        for(int i=n;i;i--) sa[c[Rank[i]]--]=i;
        tRank[sa[1]]=p=1;
        for(int i=2;i<=n;i++){
            if(Rank[sa[i]]!=Rank[sa[i-1]]) p++;
            tRank[sa[i]]=p;
        }
        for(int i=1;i<=n;i++) Rank[i]=tRank[i];
        for(int k=1;p<n;k<<=1,maxx=p){
            p=0;
            for(int i=n-k+1;i<=n;i++) tsa[++p]=i;
            for(int i=1;i<=n;i++) if(sa[i]>k) tsa[++p]=sa[i]-k;
            for(int i=0;i<=maxx;i++) c[i]=0;
            for(int i=1;i<=n;i++) tRank[i]=Rank[tsa[i]];
            for(int i=1;i<=n;i++) c[tRank[i]]++;
            for(int i=2;i<=maxx;i++) c[i]+=c[i-1];
            for(int i=n;i;i--) sa[c[tRank[i]]--]=tsa[i];
            tRank[sa[1]]=p=1;
            for(int i=2;i<=n;i++){
                if(Rank[sa[i]]!=Rank[sa[i-1]]||Rank[sa[i]+k]!=Rank[sa[i-1]+k]) p++;
                tRank[sa[i]]=p;
            }
            for(int i=1;i<=n;i++) Rank[i]=tRank[i];
        }
        for(int i=1,k=0;i<=n;i++){
            int j=sa[Rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            h[Rank[i]]=k;if(k>0) k--;
        }
    }
    int find(int x){
        return fa[x]==x?x:fa[x]=find(fa[x]);
    }
    void merge(int x,int y){
        mx[x]=max(mx[x],mx[y]);
        mn[x]=min(mn[x],mn[y]);
        siz[x]+=siz[y];
        fa[y]=x;
    }
    bool cmp(const int &a,const int &b){
        return h[a]!=h[b]?h[a]>h[b]:a<b;
    }
    int main(){
        scanf("%d%s",&n,s+1);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),s[i]-='a'-1;
        DA(30);
        for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1,nxt[i]=i+1,mx[i]=mn[i]=a[sa[i]];
        for(int i=0;i<n;i++) ans[i]=-1e18;
        sort(nxt+1,nxt+n,cmp);
        for(int i=1,x,y;i<n;i++){
            x=find(nxt[i]-1);y=find(nxt[i]);
            cnt[h[nxt[i]]]+=1LL*siz[x]*siz[y];
            ans[h[nxt[i]]]=max(ans[h[nxt[i]]],1LL*mx[x]*mx[y]);
            ans[h[nxt[i]]]=max(ans[h[nxt[i]]],1LL*mx[x]*mn[y]);
            ans[h[nxt[i]]]=max(ans[h[nxt[i]]],1LL*mn[x]*mx[y]);
            ans[h[nxt[i]]]=max(ans[h[nxt[i]]],1LL*mn[x]*mn[y]);
            merge(x,y);
        }
        for(int i=n-2;~i;i--) cnt[i]+=cnt[i+1],ans[i]=max(ans[i],ans[i+1]);
        for(int i=0;i<n;i++) printf("%lld %lld
    ",cnt[i],cnt[i]?ans[i]:0);
        return 0;
    }
  • 相关阅读:
    (Java) LeetCode 44. Wildcard Matching —— 通配符匹配
    (Java) LeetCode 30. Substring with Concatenation of All Words —— 与所有单词相关联的字串
    (Java) LeetCode 515. Find Largest Value in Each Tree Row —— 在每个树行中找最大值
    (Java) LeetCode 433. Minimum Genetic Mutation —— 最小基因变化
    (Java) LeetCode 413. Arithmetic Slices —— 等差数列划分
    (Java) LeetCode 289. Game of Life —— 生命游戏
    (Java) LeetCode 337. House Robber III —— 打家劫舍 III
    (Java) LeetCode 213. House Robber II —— 打家劫舍 II
    (Java) LeetCode 198. House Robber —— 打家劫舍
    (Java) LeetCode 152. Maximum Product Subarray —— 乘积最大子序列
  • 原文地址:https://www.cnblogs.com/shenben/p/6597821.html
Copyright © 2011-2022 走看看