BZOJ4199: [Noi2015]品酒大会

后缀数组height排序后并查集合并

也就是height较大的合并不影响较小的

num[i]=num[i+1]  ans[i]=ans[i+1]

合并时,num+=sz[x]*sz[y],ans=max(mn[x]*mn[y],mx[x]*mx[y],ans)

这种思路适应于求点对,还可以考虑启发式合并

后缀数组还有的常见思路就是二分,height分组

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 300005
 4 #define ll long long
 5 ll sum[N],ans[N];
 6 int n,val[N],fa[N],mx[N],mn[N],sz[N];
 7 int m=256,c[N],sa[N],rk[N],h[N],t1[N],t2[N];
 8 char s[N];
 9 struct Node{
10   int h,x,y;
11 }g[N];
12 int find(int x){
13   return fa[x]==x?x:fa[x]=find(fa[x]);
14 }
15 void get_sa(int n){
16   int *x=t1,*y=t2;
17   for(int i=0;i<m;i++)c[i]=0;
18   for(int i=0;i<n;i++)c[x[i]=s[i]]++;
19   for(int i=0;i<m;i++)c[i]+=c[i-1];
20   for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
21   for(int k=1;k<=n;k<<=1){
22     int p=0;
23     for(int i=n-k;i<n;i++)y[p++]=i;
24     for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
25     for(int i=0;i<m;i++)c[i]=0;
26     for(int i=0;i<n;i++)c[x[y[i]]]++;
27     for(int i=0;i<m;i++)c[i]+=c[i-1];
28     for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
29     swap(x,y);x[sa[0]]=0;p=1;
30     for(int i=1;i<n;i++)
31       x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;
32     if(p>=n)break;
33     m=p;
34   }
35 }
36 void get_height(int n){
37   for(int i=0;i<n;i++)rk[sa[i]]=i;
38   int k=0;
39   for(int i=0;i<n;i++){
40     if(k)k--;
41     int j=sa[rk[i]-1];
42     while(s[i+k]==s[j+k])k++;
43     h[rk[i]]=k;
44   }
45 }
46 bool cmp(Node x,Node y){
47   return x.h>y.h;
48 }
49 void unite(int x,int y){
50   fa[x]=y;
51   sz[y]+=sz[x];
52   mn[y]=min(mn[y],mn[x]);
53   mx[y]=max(mx[y],mx[x]);
54 }
55 int main(){
56   scanf("%d",&n);
57   scanf("%s",s);
58   for(int i=1;i<=n;i++)scanf("%d",&val[i]);
59   get_sa(n+1);
60   get_height(n+1);
61   for(int i=1;i<=n;i++)fa[i]=i,sz[i]=1,mx[rk[i-1]]=mn[rk[i-1]]=val[i];
62   for(int i=1;i<n;i++)g[i].h=h[i+1],g[i].x=i+1,g[i].y=i;
63   sort(g+1,g+n,cmp);
64   memset(sum,128,sizeof(sum));
65   for(int i=g[1].h,j=1;i>=0;i--){
66     ans[i]=ans[i+1],sum[i]=sum[i+1];
67     for(;j<n&&g[j].h==i;j++){
68       int x=find(g[j].x),y=find(g[j].y);
69       sum[i]=max(sum[i],1ll*mx[x]*mx[y]);
70       sum[i]=max(sum[i],1ll*mn[x]*mn[y]);
71       ans[i]+=1ll*sz[x]*sz[y];
72       unite(x,y);
73     }
74   }
75   for(int i=0;i<n;i++)printf("%lld %lld\n",ans[i],ans[i]==0?0:sum[i]);
76   return 0;
77 }

时间: 2024-10-11 07:02:20

BZOJ4199: [Noi2015]品酒大会的相关文章

[UOJ#131][BZOJ4199][NOI2015]品酒大会 后缀数组 + 并查集

[UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 n杯鸡尾酒.这 n杯鸡尾酒排成一行,其中第 i杯酒 (1≤i≤n ) 被贴上了一个标签 si ,每个标签都是 26 个小写英文字母之一.设 Str(l,r)表示第 l杯酒到第 r 杯酒的 r−l+1 个标签顺次连接构成的字

[bzoj4199][Noi2015]品酒大会——后缀数组

题目大意: 给定一个序列,定义两个后缀是k相似的当且仅当这两个后缀有长度为k的公共前缀. 求对任意\(r\in [0,n-1]\),\(r\)相似的后缀的对数和两个后缀乘积的最大值. 思路: 先考虑后缀数组是如何计算两个后缀的lcp,发现是对于一段连续的height取min. 于是对于制定的相似度r,height < r的位置必定是两个后缀不能越过的,于是不难发现将有height \(\ge\)r的位置给取出来,然后整个序列分成了若干个连通块,一对具有r相似的后缀必定同时在一个联通快里面. 对于

【BZOJ4199】[Noi2015]品酒大会 后缀数组+并查集

[BZOJ4199][Noi2015]品酒大会 题面:http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144 题解:听说能用SAM?SA默默水过~ 本题的实现还是非常简单的,先求出height数组,然后两杯酒'r'相似就等价于二者中间的height都>=r,于是我们将height排序,从大到小扔进去,那么所有连续的区间中的点对就都是相似的了.维护连续区间可以用并查集.统计点对个数需要维护size,统计最大乘积需要维护最(次)大(小)值,

【BZOJ-4199】品酒大会 后缀数组 + 并查集合并集合

4199: [Noi2015]品酒大会 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 436  Solved: 243[Submit][Status][Discuss] Description 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 nn 杯鸡尾酒.这 nn 杯鸡尾酒排成一行,其中第 i

Bzoj4199:[NOI2015]品酒大会

题面 Bzoj4199 Sol 后缀数组 显然的暴力就是求\(LCP\)+差分 \(40\)分 # include <bits/stdc++.h> # define RG register # define IL inline # define Fill(a, b) memset(a, b, sizeof(a)) using namespace std; typedef long long ll; const int _(3e5 + 5); IL int Input(){ RG int x =

【NOI2015】【BZOJ4199】品酒大会

Description Input Output Sample Input Sample Output HINT Source BZOJ无题面233 反正网上一堆自己下载叭>_< 对反串建SAM然后DP 第一问和AHOI2013差异完全一样 具体解法: 设Ans1[i]为lcp恰为i的后缀对数,Ans2[i]为lcp恰为i的后缀的价值乘积的最大值 建出SAM后,记录子树中后缀个数.价值的最大值.价值的最小值,然后O(n)DP即可 在SAM里DP完记得最后统计到Ans1和Ans2里去 size会

bzoj4199: [Noi2015]品酒大会 (并查集 &amp;&amp; 后缀数组)

据说用后缀自动机 + dp也能做 然而并不会 后缀数组的做法呢 就是先建个后缀数组,求出height值,此时如果直接找,复杂度是n ^ 2的,肯定会超时. 但是height大的值是不会对小的产生影响的,所以可以按height大小,从大到小合并两个区间,用并查集维护就可以了 代码如下 1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 const

BZOJ 4199: [Noi2015]品酒大会( 后缀数组 + 并查集 )

求出后缀数组后, 对height排序, 从大到小来处理(r相似必定是0~r-1相似), 并查集维护. 复杂度O(NlogN + Nalpha(N)) ----------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; ty

【uoj131】 NOI2015—品酒大会

http://uoj.ac/problem/131 (题目链接) 题意 给出一个字符串,每个后缀有一个权值${a_i}$,这些后缀两两之间存在公共前缀.问能够组成长度从0~n-1的公共前缀的后缀的方案数以及他们权值的最大乘积. Solution 听LCF说这是水题,就来做了.. lyp学长说SAM构出来后就两个东西:在自动机上dp,在后缀树上搞事情. 于是我们很容易想到在后缀树上dp(感觉在自动机上dp没什么卵用),于是我们把串反过来见后缀自动机,然后建出后缀树,在上面树形dp就可以了. 假设当