• 4199. [NOI2015]品酒大会【后缀数组+并查集】


    Description

     一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品

    酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。在大会的晚餐上,调酒师Rainbow调制了 n 杯鸡尾酒。
    这 n 杯鸡尾酒排成一行,其中第 i 杯酒 (1≤i≤n) 被贴上了一个标签 s_i ,每个标签都是 26 个小写英文字母
    之一。设 Str(l,r) 表示第 l 杯酒到第 r 杯酒的 r-l+1 个标签顺次连接构成的字符串。若 Str(p,po)=Str(q,qo
    ) ,其中 1≤p≤po≤n,1≤q≤qo≤n,p≠q,po-p+1=qo-q+1=r ,则称第 p 杯酒与第 q 杯酒是“ r 相似”的。当
    然两杯“ r 相似”(r>1)的酒同时也是“ 1 相似”、“ 2 相似”、……、“ (r-1) 相似”的。在品尝环节上,
    品酒师Freda轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中
    第 i 杯酒 (1≤i≤n) 的美味度为 a_i 。现在Rainbow公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点
    ,如果把第 p 杯酒与第 q 杯酒调兑在一起,将得到一杯美味度为 a_p a_q 的酒。现在请各位品酒师分别对于 r=
    0,1,2,?,n-1 ,统计出有多少种方法可以选出 2 杯“ r 相似”的酒,并回答选择 2 杯“ r 相似”的酒调兑可以
    得到的美味度的最大值。

    Input

    输入文件的第1行包含1个正整数 n ,表示鸡尾酒的杯数。
    第 2 行包含一个长度为 n 的字符串 S ,其中第 i 个字符表示第 i 杯酒的标签。
    第 3 行包含 n 个整数,相邻整数之间用单个空格隔开,其中第 i 个整数表示第 i 杯酒的美味度 a_i 。
    n=300,000 |a_i |≤1,000,000,000

    Output

    输出文件包括 n 行。第 i 行输出 2 个整数,中间用单个空格隔开。

    第 1 个整数表示选出两杯“ (i-1)" " 相似”的酒的方案数,
    第 2 个整数表示选出两杯“ (i-1) 相似”的酒调兑可以得到的最大美味度。
    若不存在两杯“ (i-1) 相似”的酒,这两个数均为 0 。

    Sample Input

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

    Sample Output

    45 56
    10 56
    3 32
    0 0
    0 0
    0 0
    0 0
    0 0
    0 0
    0 0
    【样例说明1】
    用二元组 (p,q) 表示第 p 杯酒与第 q 杯酒。
    0 相似:所有 45 对二元组都是 0 相似的,美味度最大的是 8×7=56 。
    1 相似: (1,8) (2,4) (2,9) (4,9) (5,6) (5,7) (5,10) (6,7) (6,10) (7,10) ,最大的 8×7=56 。
    2 相似: (1,8) (4,9) (5,6) ,最大的 4×8=32 。
    没有 3,4,5,?,9 相似的两杯酒,故均输出 0 。

    罕见的抄了一发题解……毕竟NOI原题哪有那么容易写出来的道理
    并没有什么罕见的算法,不过思路还是很巧妙的
    一开始我的想法是用单调栈,而且好像的确有这种算法的std,不过我乱搞了一下午样例都没过于是只好作罢
    改为大众并查集做法。
    首先很容易发现,对于任意一对r相似,它一定是k(0<k<r)相似的
    所以求出height数组后按其中的值排序,然后从大到小做
    当前需要处理的串为i和i-1,设前缀长度为k
    易知若将两个并查集合并,则当前的前缀在并查集中一定是最小的,所以Ans[k][0]+=两棵树size的乘积(因为任意两两前缀都是k相似的,可以配对)
    除了并查集的size,还维护一下并查集的max和min值,
    则Ans[k][1]=max(Ans[k][1],Max1*Max2,Min1*Min2)
    维护min值是为了防止有很小的复数这种情况(负负得正)
    最后因为Ans[i]也是满足Ans[i+1]的,所以做个前缀和合并一下答案就好

      1 #include<iostream>
      2 #include<cstring>
      3 #include<cstdio>
      4 #include<algorithm>
      5 #define MAXN (300000+100)
      6 using namespace std;
      7 int wt[MAXN],wa[MAXN],wb[MAXN];
      8 int SA[MAXN],Rank[MAXN],Height[MAXN];
      9 char r[MAXN];
     10 int a[MAXN],cnt;
     11 int ID[MAXN],Father[MAXN];
     12 long long Size[MAXN],Max[MAXN],Min[MAXN],Ans[MAXN][2],INF;
     13 int n,m=130;
     14 
     15 bool cmp(int *y,int a,int b,int k)
     16 {
     17     int arank1=y[a];
     18     int brank1=y[b];
     19     int arank2=a+k>=n?-1:y[a+k];
     20     int brank2=b+k>=n?-1:y[b+k];
     21     return arank1==brank1 && arank2==brank2;
     22 } 
     23 
     24 void Build_SA()
     25 {
     26     int *x=wa,*y=wb;
     27     for (int i=0;i<m;++i) wt[i]=0;
     28     for (int i=0;i<n;++i) wt[x[i]=r[i]]++;
     29     for (int i=1;i<m;++i) wt[i]+=wt[i-1];
     30     for (int i=n-1;i>=0;--i) SA[--wt[x[i]]]=i;
     31     
     32     for (int j=1;j<=n;j<<=1)
     33     {
     34         int p=0;
     35         for (int i=n-j;i<n;++i) y[p++]=i;
     36         for (int i=0;i<n;++i) if (SA[i]>=j) y[p++]=SA[i]-j;
     37         
     38         for (int i=0;i<m;++i) wt[i]=0;
     39         for (int i=0;i<n;++i) wt[x[y[i]]]++;
     40         for (int i=1;i<m;++i) wt[i]+=wt[i-1];
     41         for (int i=n-1;i>=0;--i) SA[--wt[x[y[i]]]]=y[i];
     42         
     43         m=1;swap(x,y);
     44         x[SA[0]]=0;
     45         for (int i=1;i<n;++i)
     46             x[SA[i]]=cmp(y,SA[i],SA[i-1],j)?m-1:m++;
     47         if (m>=n) break;
     48     }
     49 }
     50 
     51 void Build_Height()
     52 {
     53     for (int i=0;i<n;++i) Rank[SA[i]]=i;
     54     int k=0;
     55     Height[0]=0;
     56     for (int i=0;i<n;++i)
     57     {
     58         if (!Rank[i]) continue;
     59         if (k) k--;
     60         int j=SA[Rank[i]-1];
     61         while (r[i+k]==r[j+k]) k++;
     62         Height[Rank[i]]=k;
     63     }
     64 }
     65 
     66 int Find (int x) {return Father[x]==x?x:Father[x]=Find(Father[x]);}
     67 void Merge (int x,int y)
     68 {
     69     int f1=Find(x),f2=Find(y);
     70     
     71     int k=Height[x];
     72     Ans[k][0]+=Size[f2]*Size[f1];
     73     Ans[k][1]=max(max(Max[f2]*Max[f1],Min[f2]*Min[f1]),Ans[k][1]);
     74     
     75     Min[f1]=min(Min[f1],Min[f2]);
     76     Max[f1]=max(Max[f1],Max[f2]);
     77     Father[f2]=f1;
     78     Size[f1]+=Size[f2];
     79 }
     80 
     81 void Solve()
     82 {
     83     for (int i=0;i<n;++i) 
     84     {
     85         Father[i]=i;
     86         Size[i]=1;
     87         Max[i]=Min[i]=a[SA[i]];
     88     }
     89     for (int i=0;i<=n-1;++i)
     90         if (Find(ID[i])!=Find(ID[i]-1))
     91             Merge(ID[i],ID[i]-1);
     92 }
     93 
     94 bool cmp1(int x,int y)
     95 {
     96     return Height[x]>Height[y];
     97 }
     98 
     99 int main()
    100 {
    101     memset(&INF,0x7f,sizeof(INF));
    102     scanf("%d",&n);
    103     scanf("%s",r);
    104     for (int i=0;i<n;++i) 
    105         scanf("%d",&a[i]);
    106     Build_SA();
    107     Build_Height();
    108     for (int i=0;i<n;++i)
    109     {
    110         ID[i]=i;
    111         Ans[i][0]=0;
    112         Ans[i][1]=-INF;    
    113     }
    114     sort(ID,ID+n,cmp1);
    115     Solve();
    116     for (int i=n-2;i>=0;--i)
    117     {
    118         Ans[i][0]+=Ans[i+1][0];
    119         Ans[i][1]=max(Ans[i][1],Ans[i+1][1]);
    120     }
    121     for (int i=0;i<n;++i)
    122         printf("%lld %lld
    ",Ans[i][0],Ans[i][0]==0?0:Ans[i][1]);
    123 }
  • 相关阅读:
    ConcurrentHashMap源码分析
    HashMap源码与相关面试题
    字符串学习笔记(三)---- StringBuilder
    字符串学习笔记(二)---- StringBuffer
    Vue一些基本操作技巧
    PHP代码及命名规范
    Js返回顶部的方法
    linux下镜像网站的几种方法
    单例模式示例
    工厂模式和IOC练习
  • 原文地址:https://www.cnblogs.com/refun/p/8679198.html
Copyright © 2020-2023  润新知