luogu2178/bzoj4199 品酒大会 (SA+单调栈)

他要求的就是lcp(x,y)>=i的(x,y)的个数和a[x]*a[y]的最大值

做一下后缀和,就只要求lcp=i的了

既然lcp(x,y)=min(h[rank[x]+1],..,[h[rank[y]]])

那么我们求出来对于每一个h,以它作为最小值的区间的左右端点就可以了,这个可以用单调栈,具体做法见Neat Tree(?哪里具体了)

假设L是i左面第一个h小于等于它的,R是i右面第一个小于它的(一定要一边有=一边没有,很关键)

那就相当于lcp(x,y)=h[i] ,rank[x]∈[L,i-1],rank[y]∈[i,R-1]

数量就是这两个区间大小乘一下,最大值是max(最大值之积,最小值之积)(因为会有负的),这个可以用ST表来做

貌似并查集也能做 但我哪会啊

写的这么辣鸡 开着O2才勉强水过洛谷 哪敢到bzoj去交啊

upd:(Time Limit: 10 Sec)

  1 #include<bits/stdc++.h>
  2 #define pa pair<ll,ll>
  3 #define CLR(a,x) memset(a,x,sizeof(a))
  4 using namespace std;
  5 typedef long long ll;
  6 const int maxn=3e5+10;
  7 const ll inf=1e18;
  8
  9 inline ll rd(){
 10     ll x=0;char c=getchar();int neg=1;
 11     while(c<‘0‘||c>‘9‘){if(c==‘-‘) neg=-1;c=getchar();}
 12     while(c>=‘0‘&&c<=‘9‘) x=x*10+c-‘0‘,c=getchar();
 13     return x*neg;
 14 }
 15
 16 int N,M,deli[maxn],sa[maxn<<1],rnk[maxn<<1],rnk1[maxn<<1],tmp[maxn<<1],h[maxn<<1],cnt[maxn];
 17 ll ans2[maxn],ma[maxn][20],mn[maxn][20],dt[maxn];
 18 int stk[maxn],sh,rg[maxn];
 19 char s[maxn];
 20
 21 inline void setsa(){
 22     int i,j=0,k;
 23     for(i=1;i<=N;i++) cnt[s[i]]=1;
 24     for(i=1;i<=M;i++) cnt[i]+=cnt[i-1];
 25     for(i=N;i;i--) rnk[i]=cnt[s[i]];
 26     for(k=1;j!=N;k<<=1){
 27         // printf("%d %d %d\n",M,k,j);
 28         CLR(cnt,0);
 29         for(i=1;i<=N;i++) cnt[rnk[i+k]]++;
 30         for(i=1;i<=M;i++) cnt[i]+=cnt[i-1];
 31         for(i=N;i;i--) tmp[cnt[rnk[i+k]]--]=i;
 32         CLR(cnt,0);
 33         for(i=1;i<=N;i++) cnt[rnk[i]]++;
 34         for(i=1;i<=M;i++) cnt[i]+=cnt[i-1];
 35         for(i=N;i;i--) sa[cnt[rnk[tmp[i]]]--]=tmp[i];
 36         memcpy(rnk1,rnk,sizeof(rnk));
 37         i=2;rnk[sa[1]]=j=1;
 38         for(;i<=N;i++){
 39             if(rnk1[sa[i]]!=rnk1[sa[i-1]]||rnk1[sa[i]+k]!=rnk1[sa[i-1]+k]) j++;
 40             rnk[sa[i]]=j;
 41         }M=j;
 42     }
 43     for(i=1;i<=N;i++)
 44         sa[rnk[i]]=i;
 45 }
 46 inline void seth(){
 47     for(int i=1,j=0;i<=N;i++){
 48         if(rnk[i]==1) continue;
 49         if(j) j--;
 50         int x=sa[rnk[i]-1];
 51         while(i+j<=N&&x+j<=N&&s[i+j]==s[x+j]) j++;
 52         h[rnk[i]]=j;
 53     }
 54 }
 55
 56 inline void setma(){
 57     for(int i=N;i;i--){
 58         ma[i][0]=mn[i][0]=deli[sa[i]];
 59         for(int j=1;i+(1<<j)-1<=N;j++){
 60             int k=i+(1<<(j-1));
 61             ma[i][j]=max(ma[i][j-1],ma[k][j-1]);
 62             mn[i][j]=min(mn[i][j-1],mn[k][j-1]);
 63         }
 64     }
 65 }
 66
 67 inline pa getma(int l,int r){
 68     int k=log2(r-l+1);
 69     return make_pair(max(ma[l][k],ma[r-(1<<k)+1][k]),min(mn[l][k],mn[r-(1<<k)+1][k]));
 70 }
 71
 72 void solve(){
 73     for(int i=2;i<=N;i++){
 74         while(sh&&h[stk[sh]]>h[i])
 75             rg[stk[sh--]]=i;
 76         stk[++sh]=i;
 77     }while(sh) rg[stk[sh--]]=N+1;
 78     for(int i=N;i;i--){
 79         while(sh&&h[stk[sh]]>=h[i]){
 80             int r=rg[stk[sh]]-1;
 81             pa x=getma(i,stk[sh]-1),y=getma(stk[sh],r);
 82             ans2[h[stk[sh]]]=max(ans2[h[stk[sh]]],max(x.first*y.first,x.second*y.second));
 83             dt[h[stk[sh]]]+=1ll*(stk[sh]-i)*(r-stk[sh]+1);
 84             sh--;
 85         }
 86         stk[++sh]=i;
 87     }
 88 }
 89
 90 int main(){
 91     // freopen("testdata.in","r",stdin);
 92     // freopen("aa.out","w",stdout);
 93     int i,j,k;
 94     N=rd();
 95     scanf("%s",s+1);
 96     for(i=1;i<=N;i++)
 97         deli[i]=rd();
 98     M=127;setsa();
 99     seth();
100     setma();
101     CLR(ans2,-127);
102     solve();
103     for(i=N-1;i>=0;i--) dt[i]+=dt[i+1],ans2[i]=max(ans2[i],ans2[i+1]);
104     for(i=0;i<N;i++)
105         printf("%lld %lld\n",dt[i],dt[i]?ans2[i]:0);
106     return 0;
107 }

原文地址:https://www.cnblogs.com/Ressed/p/9833287.html

时间: 2024-07-31 19:09:15

luogu2178/bzoj4199 品酒大会 (SA+单调栈)的相关文章

Codeforces 802I Fake News (hard) (SA+单调栈) 或 SAM

原文链接http://www.cnblogs.com/zhouzhendong/p/9026184.html 题目传送门 - Codeforces 802I 题意 求一个串中,所有本质不同子串的出现次数的平方和. $|s|\leq 10^5$ 题解 首先,这一题用$SAM$做就是模板题,比较简单. 但是,本着练一练$SA$的心态,我开始了$SA+单调栈$的苦海. 真毒瘤. 这里讲一讲$SA$的做法,也是经典的做法. $SA$闭着眼睛先写了再说. 首先,我们考虑出现次数大于$1$次的子串. 考虑按

Codeforces 873F Forbidden Indices 字符串 SAM/(SA+单调栈)

原文链接https://www.cnblogs.com/zhouzhendong/p/9256033.html 题目传送门 - CF873F 题意 给定长度为 $n$ 的字符串 $s$,以及给定这个字符串每一个位置是否 "禁止结尾" 的信息. 一个字符串 $a$ 的价值为 $|a|\times f(a)$ . 其中 $f(a)$为 $a$ 在 $s$ 中的匹配次数(如果匹配的结尾为禁止结尾点,那么不算匹配成功) 问在所有的字符串 $a$ 中,$\max(|a|\times f(a)$

【POJ3415】 Common Substrings (SA+单调栈)

这道是求长度不小于 k 的公共子串的个数...很不幸,我又TLE了... 解法参考论文以及下面的链接 http://www.cnblogs.com/vongang/archive/2012/11/20/2778481.html http://hi.baidu.com/fpkelejggfbfimd/item/5c76cfcba28fba26e90f2ea6 1 const maxn=200419; 2 var 3 c,h,rank,sa,x,y,stack:array[0..maxn] of l

bzoj 4199: [Noi2015]品酒大会【后缀数组+单调栈+并查集】

用SA求出height数组,然后发现每个height值都有一个贡献区间(因为点对之间要依次取min) 用单调栈处理出区间,第一问就做完了 然后用并查集维护每个点的贡献(?),从大到小枚举height,因为这样区间是不断增大的所以并查集合并即可 #include<iostream> #include<cstdio> #include<vector> #include<algorithm> using namespace std; const int N=300

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

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

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

「AHOI 2013」差异 「SA」「单调栈」

首先明确题目要我们求什么.看到后面的 \(LCP(T_i, T_j)\) 很容易用后缀数组将其转化成 \(\min_{rk[i] < k \leq rk[j]}{height[k]}\).\((若rk[i] < rk[j])\) 考虑计算每个位置的h作为min出现的次数.很明显这个东西可以用单调栈一步求出来.那么就转为计算 \(\sum_{p = l} ^ {i} \sum_{p = i} ^ {r} (n - sa[i - 1] + 1) + (n - sa[i] + 1)\). 然后大家只

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE: