省选前大致是刷不了几道题了... 所以就找一些裸一点的题目练练板子算了= =

然而这题一点都不裸, 也并不怎么好写... 于是就浪费了将近一下午的时间... 然而还不是因为后缀数组板子不熟= =

首先这个"r相似"很显然就是lcp的值, 也就能想到后缀数组上的height... 不会后缀数组的先左转百度~

那么我们考虑如果有一个连续的区间, 它们的height值都是大于等于r的, 那么这段区间中的后缀两两"r相似".

而"r相似"的话, 也肯定有"r-1相似", "r-2相似", ... "0相似". 这样我们就会重复统计, 就会浪费时间. 所以我们不妨将这个连续的区间表示成一个点, 并查集!!

这样我们把id按照对应位置的height降序排序, 然后对于每个id, 我们把id-1这个点所在的区间和id所在的区间合并(根据height的含义, 就是表示sa[id]和sa[id-1]所对应的后缀的lcp长度..)

合并的同时维护信息即可. 说起来挺轻巧的, 其实不是很好懂.. (当然也可能是我太蒻了 理解能力差)

最后不要忘了统计的时候做一个后缀和, 比r大的答案都要统计一下.

说的很不清楚(然而其实只是用来练板子谁曾想到这破题我写了一下午呢...)

有不懂的可以去看代码看了就更不懂了Emmmm

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N=300050;
  6. const long long INF=1ll<<62;
  7. char s[N];
  8. int fa[N],sz[N],mn[N],mx[N],w[N],id[N];
  9. long long cnt[N],ans[N];
  10. int x[N],y[N],sa[N],rnk[N],cc[N],height[N],len;
  11. inline int gn(int a=0,char c=0,int f=1){
  12. for(;(c<'0'||c>'9')&&c!='-';c=getchar());
  13. if(c=='-') c=getchar(),f=-1;
  14. for(;c>47&&c<58;c=getchar()) a=a*10+c-48;
  15. return a*f;}
  16. bool hcmp(int x,int y){
  17. if(height[x]==height[y]) return x<y;
  18. return height[x]>height[y];
  19. }
  20. bool cmp(int *y,int a,int b,int k){
  21. int ra=a+k>=len?-1:y[a+k],rb=b+k>=len?-1:y[b+k];
  22. return y[a]==y[b]&&ra==rb;
  23. }
  24. void make_sa(){ int m=26;
  25. for(int i=0;i<m;++i) cc[i]=0;
  26. for(int i=0;i<len;++i) ++cc[x[i]=s[i]-'a'];
  27. for(int i=1;i<m;++i) cc[i]+=cc[i-1];
  28. for(int i=len-1;i>=0;--i) sa[--cc[x[i]]]=i;
  29. for(int k=1;k<=len;k<<=1){ int p=0;
  30. for(int i=len-k;i<len;++i) y[p++]=i;
  31. for(int i=0;i<len;++i) if(sa[i]>=k) y[p++]=sa[i]-k;
  32. for(int i=0;i<m;++i) cc[i]=0;
  33. for(int i=0;i<len;++i) ++cc[x[y[i]]];
  34. for(int i=1;i<m;++i) cc[i]+=cc[i-1];
  35. for(int i=len-1;i>=0;--i) sa[--cc[x[y[i]]]]=y[i];
  36. std::swap(x,y); m=1; x[sa[0]]=0;
  37. for(int i=1;i<len;++i)
  38. x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?m-1:m++;
  39. if(m>=len) break;
  40. }
  41. for(int i=0;i<len;++i) rnk[sa[i]]=i;
  42. }
  43. void make_height(){ int k=0;
  44. for(int i=0;i<len;++i){
  45. if(!rnk[i]) continue;
  46. int j=sa[rnk[i]-1];
  47. if(k) --k;
  48. while(s[i+k]==s[j+k]) ++k;
  49. height[rnk[i]]=k;
  50. }
  51. }
  52. int find(int x){ if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x];}
  53. void merge(int x,int y){
  54. sz[y]+=sz[x]; fa[x]=y;
  55. mn[y]=min(mn[x],mn[y]);
  56. mx[y]=max(mx[x],mx[y]);
  57. }
  58. int main(){
  59. len=gn();
  60. scanf("%s",s); len=strlen(s);
  61. make_sa(); make_height();
  62. for(int i=0;i<len;++i)
  63. w[i]=gn();
  64. for(int i=0;i<len;++i){
  65. fa[i]=i; id[i]=i;
  66. mx[i]=w[sa[i]]; mn[i]=w[sa[i]];
  67. sz[i]=1; ans[i]=-INF;
  68. } ::sort(id+1, id+len, hcmp);
  69. for(int i=1;i<len;++i){
  70. int x=find(id[i]-1),y=find(id[i]);
  71. cnt[height[id[i]]]+=1ll*sz[x]*sz[y];
  72. ans[height[id[i]]]=max(ans[height[id[i]]],1ll*mn[x]*mn[y]);
  73. ans[height[id[i]]]=max(ans[height[id[i]]],1ll*mn[x]*mx[y]);
  74. ans[height[id[i]]]=max(ans[height[id[i]]],1ll*mx[x]*mn[y]);
  75. ans[height[id[i]]]=max(ans[height[id[i]]],1ll*mx[x]*mx[y]);
  76. merge(x,y);
  77. }
  78. for(int i=len-2;i>=0;--i)
  79. cnt[i]+=cnt[i+1],ans[i]=max(ans[i],ans[i+1]);
  80. for(int i=0;i<len;++i)
  81. printf("%lld %lld\n",cnt[i],cnt[i]?ans[i]:0);
  82. }

【学术篇】NOI2015 品酒大会 后缀数组+并查集的更多相关文章

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

    [UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个 ...

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

    [BZOJ4199][Noi2015]品酒大会 题面:http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144 题解:听说能用SAM?SA默默 ...

  3. [NOI2015] 品酒大会 - 后缀数组,并查集,STL,启发式合并

    [NOI2015] 品酒大会 Description 对于每一个 \(i \in [0,n)\) 求有多少对后缀满足 LCP 长度 \(\le i\) ,并求满足条件的两个后缀权值乘积的最大值. So ...

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

    求出后缀数组后, 对height排序, 从大到小来处理(r相似必定是0~r-1相似), 并查集维护. 复杂度O(NlogN + Nalpha(N)) ------------------------- ...

  5. NOI 2015 品酒大会 (后缀数组+并查集)

    题目大意:略 40分暴力还是很好写的,差分再跑个后缀和 和 后缀最大值就行了 一种正解是后缀数组+并查集 但据说还有后缀数组+单调栈的高端操作蒟蒻的我当然不会 后缀数组求出height,然后从大到小排 ...

  6. Uoj #131. 【NOI2015】品酒大会 后缀数组,并查集

    #131. [NOI2015]品酒大会 统计 描述 提交 自定义测试 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项, ...

  7. BZOJ 4199: [Noi2015]品酒大会 [后缀数组 带权并查集]

    4199: [Noi2015]品酒大会 UOJ:http://uoj.ac/problem/131 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品 ...

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

    4199: [Noi2015]品酒大会 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 436  Solved: 243[Submit][Status] ...

  9. BZOJ.4199.[NOI2015]品酒大会(后缀数组 单调栈)

    BZOJ 洛谷 后缀自动机做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 显然只需要考虑极长的相同子串的贡献,然后求后缀和/后缀\(\max\)就可以了. 对于相同子串,我们能想 ...

随机推荐

  1. ng2-file-upload插件在ionic3中的使用方法

    本文主要说明在ionic3中使用ng2-file-upload插件,上传多种类型文件(图片.word.excel.ppt.mp4等)的使用方法. 1.html代码: <button ion-bu ...

  2. Linux学习笔记0-CentOS7关闭防火墙

    关闭防火墙 systemctl stop firewalld.service //停止firewall systemctl disable firewalld.service //禁止firewall ...

  3. 关于GeneXus封装方法Model的方法

     最近 刚从外地出差回来 工作任务不是很重 能够抽点时间记点东西 下午花了2个多钟头尝试了一下GeneXus的封装方法的功能,这里记一下便于自己以后查看.我们在许多项目中或多或少都会有着重复代码编写的 ...

  4. shell条件测试语句

  5. 一、Gulp

    开发和部署前端项目: 在开发Web应用中为加速客户端资源响应(js和css),减少对js和css的请求,通过bundles来实现. 在ASP.NET5中放弃该特性,被其他类似的组件所代替.比如使用Gu ...

  6. 转载:tomcat性能的优化

    考虑一下这种场景,你开发了一个应用,它有十分 优秀的布局设计,最新的特性以及其它的优秀特点.但是在性能这方面欠缺,不管这个应用如何都会遭到客户拒绝.客户总是期望它们的应用应该有更好的性能.如 果你在产 ...

  7. day13 python生成器函数 推导式 生成器

    day13 python   一.生成器     生成器的本质就是迭代器     生成器的特点和迭代器一样. 取值方式和迭代器一样(__next__())     由生成器函数或生成器表达式来创建   ...

  8. c++后台开发面试常见知识点总结(五)场景设计

    搜索引擎的实现,会用到哪些重要的数据结构 设计实现一个HTTP代理服务器 / web服务器 / FTP服务器/ 设计实现cache缓存web服务器的网页访问记录 把一个文件快速下发到100w个服务器 ...

  9. [转]C# CancellationTokenSource 终止线程

    我们在多线程中通常使用一个bool IsExit类似的代码来控制是否线程的运行与终止,其实使用CancellationTokenSource来进行控制更为好用,下面我们将介绍CancellationT ...

  10. PHP chdir() 函数

    实例 改变当前的目录: <?php// Get current directoryecho getcwd() . "<br>"; // Change direct ...