字符串(后缀数组||SAM):NOI2015 品酒大会

  这道题可以转化为计数类问题。

  若使用后缀数组,那么答案就是所有位置二元组(i,j)的lcp对0~lcp答案段的贡献。然后发现若一个二元组有x的贡献,那么对x-1有同样的贡献,考虑先求出lcp(max)的答案,再传给lcp(max-1)等等,复杂度是O(N)的。

  若用SAM,那么需要求的答案在x与fa[x]的转移之间,因为后缀自动机中每个点所代表的出现位置和出现次数是相等的,可以合并,事实上还是用到了后缀数组的叠加的原理,从叶子节点一路传上去……………………………………………

  还有要注意INF要足够大。

  1 #include <algorithm>
  2 #include <iostream>
  3 #include <cstring>
  4 #include <cstdio>
  5 using namespace std;
  6 const int maxn=300010;
  7 const long long INF=3000000000000000000LL;
  8 int rank[maxn],sa[maxn],r[maxn],lcp[maxn];
  9 int Wa[maxn],Wb[maxn],Wv[maxn],Ws[maxn];
 10
 11 bool cmp(int *p,int i,int j,int l){
 12     return p[i]==p[j]&&p[i+l]==p[j+l];
 13 }
 14
 15 void DA(int n,int m){
 16     int i,h,p=1,*x=Wa,*y=Wb;
 17     for(i=0;i<m;i++)Ws[i]=0;
 18     for(i=0;i<n;i++)Ws[x[i]=r[i]]++;
 19     for(i=1;i<m;i++)Ws[i]+=Ws[i-1];
 20     for(i=n-1;i>=0;i--)sa[--Ws[x[i]]]=i;
 21
 22
 23     for(h=1;p<n;m=p,h<<=1){
 24         for(i=n-h,p=0;i<n;i++)y[p++]=i;
 25         for(i=0;i<n;i++)
 26             if(sa[i]>=h)y[p++]=sa[i]-h;
 27         for(i=0;i<m;i++)Ws[i]=0;
 28         for(i=0;i<n;i++)Ws[Wv[i]=x[y[i]]]++;
 29         for(i=1;i<m;i++)Ws[i]+=Ws[i-1];
 30         for(i=n-1;i>=0;i--)sa[--Ws[Wv[i]]]=y[i];
 31         for(p=1,swap(x,y),x[sa[0]]=0,i=1;i<n;i++)
 32             x[sa[i]]=cmp(y,sa[i-1],sa[i],h)?p-1:p++;
 33     }
 34 }
 35
 36 void LCP(int n){
 37     int i,j,k=0;
 38     for(i=1;i<=n;i++)rank[sa[i]]=i;
 39     for(i=0;i<n;lcp[rank[i++]]=k)
 40         for(k?k--:k,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
 41 }
 42
 43 int n;
 44 char s[maxn];
 45
 46 struct Node{
 47     int x,y,v;
 48     Node(int x_=0,int y_=0,int v_=0):x(x_),y(y_),v(v_){}
 49     friend bool operator <(Node a,Node b){
 50         return a.v>b.v;
 51     }
 52 }st[maxn];
 53 int w[maxn],fa[maxn];
 54 long long mx[maxn],mn[maxn];
 55 long long sz[maxn],ans[maxn],sum[maxn];
 56
 57 int Find(int x){
 58     return fa[x]==x?x:fa[x]=Find(fa[x]);
 59 }
 60
 61 int main(){
 62 #ifndef ONLINE_JUDGE
 63     freopen("savour.in","r",stdin);
 64     freopen("savour.out","w",stdout);
 65 #endif
 66     scanf("%d%s",&n,s);
 67     for(int i=0;i<n;i++){
 68         scanf("%d",&w[i]);
 69         r[i]=s[i];
 70     }
 71     DA(n+1,128);LCP(n);
 72
 73     for(int i=2;i<=n;i++)
 74         st[i-1]=Node(sa[i],sa[i-1],lcp[i]);
 75     sort(st+1,st+n);
 76
 77     for(int i=0;i<n;i++){
 78         fa[i]=i;sz[i]=1;
 79         mx[i]=mn[i]=w[i];
 80         sum[i]=-INF;
 81     }
 82
 83     for(int i=st[1].v,j=1;i>=0;i--){
 84         ans[i]=ans[i+1];
 85         sum[i]=sum[i+1];
 86         while(j<n&&st[j].v==i){
 87             int x=Find(st[j].x),y=Find(st[j].y);
 88             sum[i]=max(sum[i],mx[x]*mx[y]);
 89             sum[i]=max(sum[i],mn[x]*mn[y]);
 90             ans[i]+=sz[x]*sz[y];
 91             fa[x]=y;sz[y]+=sz[x];
 92             mn[y]=min(mn[x],mn[y]);
 93             mx[y]=max(mx[x],mx[y]);
 94             j+=1;
 95         }
 96     }
 97
 98     for(int i=0;i<n;i++){
 99         if(ans[i]==0)sum[i]=0;
100         printf("%lld %lld\n",ans[i],sum[i]);
101     }
102     return 0;
103 }

  这个是SAM,小了一个log的复杂度,实际上没快多少……

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 using namespace std;
 5 const int maxn=600010;
 6 const long long INF=1LL<<60;
 7 int n,cnt,last,ord[maxn],c[maxn];
 8 int fa[maxn],ch[maxn][26],len[maxn];
 9 long long tmp[maxn],mx[maxn],mn[maxn],w[maxn];
10 long long num[maxn],tot[maxn],t1[maxn],t2[maxn];
11 char s[maxn];
12
13 struct SAM{
14     SAM(){cnt=last=1;}
15     void Insert(int c,int t){
16         int p=last,np=last=++cnt;tmp[np]=1;
17         len[np]=len[p]+1;mx[cnt]=mn[cnt]=w[t];
18         while(p&&!ch[p][c])ch[p][c]=np,p=fa[p];
19         if(!p)fa[np]=1;
20         else{
21             int q=ch[p][c],nq;
22             if(len[q]==len[p]+1)
23                 fa[np]=q;
24             else{
25                 len[nq=++cnt]=len[p]+1;
26                 mx[cnt]=-INF;mn[cnt]=INF;
27                 memcpy(ch[nq],ch[q],sizeof(ch[q]));
28                 fa[nq]=fa[q];fa[q]=fa[np]=nq;
29                 while(ch[p][c]==q)ch[p][c]=nq,p=fa[p];
30             }
31         }
32         return;
33     }
34
35     void Solve(){
36         for(int i=1;i<=cnt;i++)c[len[i]]++;
37         for(int i=1;i<=len[last];i++)c[i]+=c[i-1];
38         for(int i=1;i<=cnt;i++)ord[c[len[i]]--]=i;
39         for(int i=1;i<=cnt;i++)t2[i]=num[i]=-INF;
40         mn[1]=INF;mx[1]=-INF;
41
42         for(int i=cnt;i>=1;i--){
43             int p=ord[i];
44             t1[fa[p]]+=tmp[fa[p]]*tmp[p];
45             tmp[fa[p]]+=tmp[p];
46             if(mn[fa[p]]!= INF)t2[fa[p]]=max(t2[fa[p]],mn[p]*mn[fa[p]]);
47             if(mx[fa[p]]!=-INF)t2[fa[p]]=max(t2[fa[p]],mx[p]*mx[fa[p]]);
48             mn[fa[p]]=min(mn[fa[p]],mn[p]);
49             mx[fa[p]]=max(mx[fa[p]],mx[p]);
50         }
51
52         for(int i=1;i<=cnt;i++){
53             tot[len[i]]+=t1[i];
54             num[len[i]]=max(num[len[i]],t2[i]);
55         }
56
57         for(int i=n-2;i>=0;i--){
58             tot[i]+=tot[i+1];
59             num[i]=max(num[i],num[i+1]);
60         }
61
62         for(int i=0;i<n;i++)
63             printf("%lld %lld\n",tot[i],tot[i]?num[i]:0);
64     }
65 }sam;
66
67 int main(){
68     freopen("savour.in","r",stdin);
69     freopen("savour.out","w",stdout);
70     scanf("%d%s",&n,s);
71     for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
72     for(int i=n;i;i--)sam.Insert(s[i-1]-‘a‘,i);
73     sam.Solve();
74     return 0;
75 }
时间: 2024-09-29 23:26:59

字符串(后缀数组||SAM):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]品酒大会 后缀数组+并查集

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

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

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

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

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

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

Bzoj4556: [Tjoi2016&amp;Heoi2016]字符串 后缀数组

4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 169  Solved: 87[Submit][Status][Discuss] Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了 一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE O,嫁给高富帅,走上人生巅峰.

[XSY 1516] 兔子的字符串 后缀数组

题意 给定一个字符串 $S$ . 按照某种方式, 将字符串 $S$ 化成不超过 $K$ 段 $S_1, S_2, ..., S_K$ . 每段 $S_i$ 有字典序最大的子串 $C_i$ . 最小化 $C_i$ 的最大值. $N \le 200000$ . 分析 通过后缀数组, 先二分后缀, 再二分长度, 实现二分所有的字符串. 判定则可以贪心取, 利用后缀数组的信息, 记录 v[i] 表示位置 i 不能与位置 v[i] 在同一段中. 实现 #include <cstdio> #include

●UOJ 131 [NOI2015] 品酒大会

题链: http://uoj.ac/problem/131 题解: 网上大多数的方法都是用并查集维护.这里呢,给出另一种自己YY的解法(但实际上本质差不多吧): 后缀数组,RMQ,单调栈 1).预处理 1].首先对字符串后缀排序,得到 sa[i],rank[i],height[i]    2].然后维护出 L[i]:表示在后缀数组中,排名最小(记其排名为 L[i])的后缀与排名为 i的后缀的LCP>=hei[i]    同理 R[i]:表示在后缀数组中,排名最大(记其排名为 R[i])的后缀与排

bzoj4556: [Tjoi2016&amp;Heoi2016]字符串 (后缀数组加主席树)

题目是给出一个字符串,每次询问一个区间[a,b]中所有的子串和另一个区间[c,d]的lcp最大值,首先求出后缀数组,对于lcp的最大值肯定是rank[c]的前驱和后继,但是对于这个题会出现问题,就是题目中有区间的限制. For example: 5 1 aaaab 1 2 3 5 对于这个样例,如果直接找到aab的前驱是 aaab,然后由于区间的原因答案是1,但是如果我们再往前找的话,找到aaaab,答案会变成2.那就出现了错误.考虑一下怎么做可以去除这种影响呢? 我们可以二分一下,首先对于[a