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\n",Ans[i][0],Ans[i][0]==0?0:Ans[i][1]);
123 }
posted @ 2018-03-30 23:03  Refun  阅读(1278)  评论(1编辑  收藏  举报