【NOI2015】品酒大会

令${str\left(i,j \right)}$为区间${[l,r]}$所表示的子串。

对于任意$r$

一,求${\sum _{i=1}^{n}\sum _{j=i+1}^{n}[str(i,i+r-1)=str(j,j+r-1)]}$

二,求${ans[r]=max(val[i]*val[j])}$,且${str(i,i+r-1)=str(j,j+r-1)}$



  直接用SAM构出后缀树,然后一个子串一定是原串的一个后缀的前缀,然后找到后缀树中每一个后缀对应的节点,每一对后缀的LCP对应了后缀树中的相应点的LCA的len(即代表的最长串的长度),然后做一遍树形统计统计第一问答案,第二问答案即要记一下子树最大值最小值即可。



  

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<cstdlib>
  6 #include<cmath>
  7 #include<cstring>
  8 using namespace std;
  9 #define maxn 1000100
 10 #define llg long long
 11 #define SIZE 26
 12 #define inf (((llg)1e18)+10)
 13 #define yyj(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
 14 llg n,m,ans1[maxn<<1],ans2[maxn<<1],val[maxn],zhu[maxn<<1],quan[maxn<<1],tail;
 15 llg minl[maxn<<1],maxl[maxn<<1],ans3[maxn<<1];
 16 char s[maxn];
 17
 18 struct SAM
 19 {
 20     struct
 21     {
 22         llg len,f,ch[SIZE];
 23         void init()
 24         {
 25             len=0,f=-1;
 26             memset(ch,0xff,sizeof(ch));
 27         }
 28     }e[maxn<<1];
 29
 30     vector<llg>a[maxn<<1];
 31     llg idx,last,size[maxn<<1];
 32
 33     void init(){idx=last=0; e[idx++].init(); memset(size,0,sizeof(size)); }
 34
 35     llg newnode(){e[idx].init(); return idx++;}
 36
 37     void insert(llg c)
 38     {
 39         llg end=newnode(),tmp=last;
 40         e[end].len=e[last].len+1; size[end]=1; zhu[end]=1; quan[end]=val[tail]; maxl[end]=val[tail],minl[end]=val[tail]; tail++;
 41         for (;tmp!=-1 && e[tmp].ch[c]==-1;tmp=e[tmp].f)
 42             e[tmp].ch[c]=end;
 43         if (tmp==-1) e[end].f=0;
 44         else
 45         {
 46             llg nxt=e[tmp].ch[c];
 47             if (e[tmp].len+1==e[nxt].len) e[end].f=nxt;
 48             else
 49             {
 50                 llg np=newnode();// maxl[np]=maxl[end],minl[np]=minl[end];
 51                 e[np]=e[nxt];
 52                 e[np].len=e[tmp].len+1;
 53                 e[nxt].f=e[end].f=np;
 54                 for (;tmp!=-1 && e[tmp].ch[c]==nxt;tmp=e[tmp].f) {e[tmp].ch[c]=np;}
 55             }
 56         }
 57         last=end;
 58         //    cout<<last<<endl;
 59     }
 60
 61     void link_fa() {for (llg i=1;i<idx;i++) a[e[i].f].push_back(i);}
 62
 63     void dp(llg x)
 64     {
 65         llg w=a[x].size(),v;
 66         for (llg i=0;i<w;i++) dp(a[x][i]);
 67         for (llg i=0;i<w;i++)
 68         {
 69             v=a[x][i];
 70             ans1[e[x].len]+=size[x]*size[v];
 71             if (1)
 72             {
 73                 if (maxl[v]!=-1*inf)
 74                 {
 75                     if (maxl[x]!=-1*inf) ans2[e[x].len]=max(ans2[e[x].len],maxl[x]*maxl[v]);
 76                     maxl[x]=max(maxl[x],maxl[v]);
 77                 }
 78                 if (minl[v]!=inf)
 79                 {
 80                     if (minl[x]!=inf) ans2[e[x].len]=max(ans2[e[x].len],minl[x]*minl[v]);
 81                     minl[x]=min(minl[x],minl[v]);
 82                 }
 83             }
 84             size[x]+=size[v];
 85         }
 86         //ans1[e[x].len]+=size[x];
 87     }
 88 }sam;
 89
 90 int main()
 91 {
 92     yyj("a");
 93     cin>>n;
 94     sam.init();
 95     scanf("%s",s);
 96     for (llg i=0;i<=n*2;i++) ans2[i]=maxl[i]=-1*inf,minl[i]=inf,ans3[i]=inf*-1;
 97     for (llg i=0;i<n;i++) scanf("%lld",&val[i]);
 98     for (llg i=0;i<(n+1)/2;i++) swap(val[i],val[n-i-1]);
 99     for (llg i=n-1;i>=0;i--)
100         sam.insert(s[i]-‘a‘);
101     sam.link_fa();
102     sam.dp(0);
103     for (llg i=n-1;i>=1;i--)
104     {
105         ans1[i-1]+=ans1[i];
106         if (ans1[i]) ans2[i-1]=max(ans2[i-1],ans2[i]);
107     }
108 //    for (llg i=0;i<n;i++) ans3[sam.e[i].len]=max(ans3[sam.e[i].len],ans2[i]);
109     for (llg i=0;i<n;i++) printf("%lld %lld\n",ans1[i],ans1[i]?ans2[i]:0);
110     return 0;
111 }
时间: 2024-08-09 02:17:58

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

【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: [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就可以了. 假设当

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

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

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 =

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