【NOI2015】品酒大会 SAM

论没有看完题目的危害,以及预处理不做号的危害。两个小时过去了…………………………………………

不难。把字符串逆序建后缀自动机,利用fail边得到后缀树。(理解上的,实际上没有在树上跑。)

由于后缀树上每一个叶子节点所代表的串是原串的后缀。现在逆序之后,就变成了前缀。那

因为一个节点x的sz数组表示这个节点所代表的所有的串都出现了sz[x]次。而且该串所代表的字符串长度为 dep[fa[x]] + 1 ~ dep[x] 所以理论上考虑一个节点对答案的贡献是是要对 dep[fa[x]+1] ~ dep[x]都加一次的。但是很显然有前缀和的思想,那么就不用了。

总而言之。后缀自动机的思想就是用一个长串已有的信息,和它的后缀的信息汇总。进而得出全部的信息。

(特别地,对于任意的 1≤p,q≤n1≤p,q≤n,p≠qp≠q,第 pp 杯酒和第 qq杯酒都是“0相似”的。)不知道是否有人和我一样没看题。而且有些maxl,minl没初始化,WA了好久。心好累。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define rep(i,j,k) for(register int i = j; i <= k; i++)
 5 #define dow(i,j,k) for(register int i = j; i >= k; i--)
 6 #define maxn 302333
 7 #define ll long long
 8 #define inf 0x7fffffff
 9 using namespace std;
10
11 inline int read() {
12     int s = 0, t = 1; char c = getchar();
13     while( !isdigit(c) ) { if( c == ‘-‘ ) t = -1; c = getchar(); }
14     while( isdigit(c) ) s = s * 10 + c - 48, c = getchar();
15     return s * t;
16 }
17
18 int u, p, last = 1, tot = 1, v, nv, dep[maxn<<1], fa[maxn<<1], sz[maxn<<1];
19 int maxl[maxn<<1][2], minl[maxn<<1][2], son[maxn<<1][26];
20 inline void extend(int c,int key) {
21     u = last; last = p = ++tot; dep[p] = dep[u] + 1; sz[p] = 1;
22     maxl[p][0] = minl[p][0] = key; //cout<<"B "<<p<<" "<<key<<endl;
23     maxl[p][1] = -2e9; minl[p][1] = 2e9;
24     while( u && !son[u][c] ) son[u][c] = p, u = fa[u];
25     if( !u ) fa[p] = 1;
26     else {
27         v = son[u][c];
28         if( dep[v] == dep[u] + 1 ) fa[p] = v;
29         else {
30             nv = ++tot; dep[nv] = dep[u] + 1;
31             fa[nv] = fa[v]; fa[v] = fa[p] = nv;
32             maxl[tot][0] = maxl[tot][1] = -2e9;
33             minl[tot][0] = minl[tot][1] = 2e9;
34             memcpy(son[nv],son[v],sizeof(son[v]));
35             while( son[u][c] == v ) son[u][c] = nv, u = fa[u];
36         }
37     }
38 }
39
40 int q[maxn<<1], tong[maxn<<1] = {0}, key[maxn];
41 inline void pre() {
42     rep(i,1,tot) tong[dep[i]]++;
43     rep(i,1,tot) tong[i] += tong[i-1];
44     dow(i,tot,1) q[tong[dep[i]]--] = i;
45     int x;
46     dow(i,tot,1) x = q[i], sz[fa[x]] += sz[x];
47     dep[0] = -1;
48 }
49 ll cnt[maxn], ans[maxn]; char c[maxn];
50 inline void updatemax(int x,int v) {
51 //    cout<<"M "<<x<<" "<<v<<" "<<maxl[x][1]<<" ";
52     maxl[x][1] = max(maxl[x][1],v);
53 //    cout<<maxl[x][1]<<endl;
54     if( maxl[x][1] > maxl[x][0] ) swap(maxl[x][1],maxl[x][0]);
55 }
56 inline void updatemin(int x,int v) {
57 //    cout<<"I "<<x<<" "<<v<<" "<<minl[x][1]<<" ";
58     minl[x][1] = min(minl[x][1],v);
59 //    cout<<minl[x][1]<<endl;
60     if( minl[x][1] < minl[x][0] ) swap(minl[x][1],minl[x][0]);
61 }
62
63 int main() {
64     int n = read(); scanf("%s", c+1);
65     maxl[1][0] = maxl[1][1] = -2e9; minl[1][0] = minl[1][1] = 2e9;
66     rep(i,1,n) key[i] = read();
67     dow(i,n,1) extend(c[i]-‘a‘,key[i]);
68     pre(); int x;
69     memset(ans,128,sizeof(ans));
70     memset(cnt,0,sizeof(cnt));
71     dow(i,tot,1) {
72         x = q[i];
73         if( sz[x] > 1 ) {
74             cnt[dep[x]] += 1ll * (sz[x] - 1) * sz[x] / 2;
75             if( dep[fa[x]] >= 0 ) cnt[dep[fa[x]]] -= 1ll * (sz[x] - 1) * sz[x] / 2;
76             ans[dep[x]] = max(ans[dep[x]],max(1ll*maxl[x][0]*maxl[x][1],1ll*minl[x][0]*minl[x][1]));
77         }
78         updatemax(fa[x],maxl[x][0]);
79         updatemax(fa[x],maxl[x][1]);
80         updatemin(fa[x],minl[x][0]);
81         updatemin(fa[x],minl[x][1]);
82     }
83     dow(i,n-1,0) cnt[i] += cnt[i+1];
84     dow(i,n-1,0) ans[i] = max(ans[i],ans[i+1]);
85     rep(i,0,n-1) if( !cnt[i] ) ans[i] = 0;
86     rep(i,0,n-1) printf("%lld %lld\n", cnt[i],ans[i]);
87  //   x = read();
88     return 0;
89 }
时间: 2024-10-11 16:56:47

【NOI2015】品酒大会 SAM的相关文章

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

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

[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 个标签顺次连接构成的字

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

这道题可以转化为计数类问题. 若使用后缀数组,那么答案就是所有位置二元组(i,j)的lcp对0~lcp答案段的贡献.然后发现若一个二元组有x的贡献,那么对x-1有同样的贡献,考虑先求出lcp(max)的答案,再传给lcp(max-1)等等,复杂度是O(N)的. 若用SAM,那么需要求的答案在x与fa[x]的转移之间,因为后缀自动机中每个点所代表的出现位置和出现次数是相等的,可以合并,事实上还是用到了后缀数组的叠加的原理,从叶子节点一路传上去…………………………………………… 还有要注意INF要足

【uoj131】 NOI2015—品酒大会

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

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

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

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 #

●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])的后缀与排

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

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4199 题意: 给你一个长度为n的字符串s,和一个长为n的数组v. 对于每个整数r∈[0,n-1]: (1)问你有多少对后缀(suffix(i), suffix(j)),满足LCP(suffix(i), suffix(j)) >= r (2)输出mul[r] = max(v[i]*v[j]),其中i,j满足(1)的条件 题解: 先考虑第(1)问. 由于LCP只受连续的一段height中最小

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 =