题目

【题目描述】

由于你成功地在 $ \text{1 s} $ 内算出了上一题的答案,英雄们很高兴并邀请你加入了他们的游戏。然而进入游戏之后你才发现,英雄们打的游戏和你想象的并不一样……

英雄们打的游戏是这样的:首先系统会产生(**注意不一定是随机产生**)一个字符串,然后每个英雄就会开始根据自己分到的任务计算这个字符串的某些特征,谁先算出自己的答案谁就是胜者。

由于打游戏的英雄比较多,因此英雄们分到的任务也就可能很奇怪。比如你分到的这个任务就是这样:

定义这个字符串以第 $ i $ 个字符开头的后缀为后缀 $ i $ (编号从 $ 1 $ 开始),每个后缀 $ i $ 都有一个权值 $ w_i $ ,同时定义两个后缀 $ i,j $ ($ i\ne j $) 的贡献为它们的最长公共前缀长度加上它们权值的异或和,也就是 $ \mathrm{LCP}(i,j)+(w_i \mathbin{\text{xor}} w_j) $ 。而你的任务就是,求出这个字符串的所有后缀两两之间贡献的最大值。

【输入格式】

第一行一个正整数 $ n $,表示字符串的长度。
第二行一个仅包含小写英文字母的字符串,即系统产生的字符串。
第三行 $ n $ 个非负整数 $ w_i $,分别表示后缀 $ 1 $ ~ $ n $ 的权值。

【输出格式】

一行一个整数表示答案。

【样例输入】

7
acbabac
0 1 5 6 4 2 3

【样例输出】

7

【样例解释】

后缀 $ 1 $ 和后缀 $ 4 $ 的贡献是 $ 1+(0\;\text{xor}\;6)=7 $ ,不难验证它们的贡献确实是所有可能的贡献中最大的。

【数据范围与提示】

对于 $ 30\% $ 的数据,$ n\le 5\times 10^3 $;
对于另 $ 30\% $ 的数据,保证字符串是随机生成的;
对于另 $ 10\% $ 的数据,$ w_i=0 $;
对于另 $ 10\% $ 的数据,$ w_i\le 1 $;
对于 $ 100\% $ 的数据,$ n\le 10^5 $,$ w_i< n $ 。

题解

求任意两个后缀的 LCP 很容易想到后缀数组

记排序后的两个相邻后缀 $ i-1,i $ 的 LCP 为 $ height[i] $

那么任意的两个后缀 $ i,j $ 的 LCP 为 $ min_{k=i}^{j}height[k] $

至于求 $W$ 的异或值考虑在 tire 树上贪心

当 $ height[i] $ 为 $[l,r] $ 的最小值时才会对该区间有影响,那么考虑如何用 $ height[i] $ 来更新答案

将 $ height[i] $ 从大到小排序后,合并 $ P_i $ 和 $ P_{i-1} $ 属于的两个区间 $ [L_{P_i},R_{p_i}] $ 和 $ [L_{P_{i-1}},R_{P_{i-1}}] $,此时保证 $ height[i] $ 为两个区间中的最小值(因为比 $ i $ 大的已经合并了)

然后在 tire 树上启发式合并两个区间即可,贪心选取答案

时间效率:$ O(n \log n+n \log^2n)$

至于 SA 的排序可以用倍增法或者二分哈希都可以(也就多一个 $ \log $,反正启发式合并也要 $ \log^2 $)

为什么我一点都没有感觉到套路,可能是题写太少了

代码

  1. #include<bits/stdc++.h>
  2. #define LL long long
  3. #define _(d) while(d(isdigit(ch=getchar())))
  4. using namespace std;
  5. int R(){
  6. int x;bool f=;char ch;_(!)if(ch=='-')f=;x=ch^;
  7. _()x=(x<<)+(x<<)+(ch^);return f?x:-x;}
  8. const int N=2e5+;
  9. int n,m,w[N],p[N],ht[N],rak[N],tp[N],sa[N],tax[N],ans;
  10. char ch[N];
  11. void Qsort(){
  12. for(int i=;i<=m;i++)tax[i]=;
  13. for(int i=;i<=n;i++)tax[rak[tp[i]]]++;
  14. for(int i=;i<=m;i++)tax[i]+=tax[i-];
  15. for(int i=n;i>=;i--)sa[tax[rak[tp[i]]]--]=tp[i];
  16. }
  17. void SA(){
  18. m=,Qsort();
  19. for(int l=,p=;l<=n;l<<=){
  20. for(int i=n-l+;i<=n;i++)tp[++p]=i;
  21. for(int i=;i<=n;i++)if(sa[i]>l)tp[++p]=sa[i]-l;
  22. Qsort(),swap(rak,tp);
  23. rak[sa[]]=p=;
  24. for(int i=;i<=n;i++)
  25. rak[sa[i]]=(tp[sa[i-]]==tp[sa[i]]&&tp[sa[i-]+l]==tp[sa[i]+l])?p:++p;
  26. if(p>n)break;
  27. m=p+,p=;
  28. }
  29. int k=;
  30. for(int i=,j;i<=n;i++){
  31. j=sa[rak[i]-];
  32. if(k)k--;
  33. while(ch[j+k]==ch[i+k])k++;
  34. ht[rak[i]]=k;
  35. }
  36. }
  37. bool cmp(int a,int b){return ht[a]>ht[b];}
  38. int li[N],ri[N],fa[N],rt[N],tot,tr[N*][];
  39. int query(int k,int dep,int val){
  40. if(!~dep)return ;
  41. if(tr[k][((val>>dep)&)^])
  42. return (<<dep)+query(tr[k][((val>>dep)&)^],dep-,val);
  43. else return query(tr[k][(val>>dep)&],dep-,val);
  44. }
  45. void insert(int &k,int dep,int val){
  46. if(!k)k=++tot;
  47. if(~dep)insert(tr[k][(val>>dep)&],dep-,val);
  48. }
  49. int merge(int x,int y){
  50. int res=;
  51. if(ri[x]-li[x]<ri[y]-li[y])swap(x,y);
  52. for(int i=li[y];i<=ri[y];i++)
  53. res=max(res,query(rt[x],,w[sa[i]]));
  54. for(int i=li[y];i<=ri[y];i++)
  55. insert(rt[x],,w[sa[i]]);
  56. fa[y]=x,li[x]=min(li[x],li[y]),ri[x]=max(ri[x],ri[y]);
  57. return res;
  58. }
  59. int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
  60. int main(){
  61. n=R(),scanf("%s",ch+);
  62. for(int i=;i<=n;i++)
  63. rak[i]=ch[i]-'a',p[i]=tp[i]=i,w[i]=R();
  64. SA(),sort(p+,p+n+,cmp);
  65. for(int i=;i<=n;i++)
  66. li[i]=ri[i]=fa[i]=i,insert(rt[i],,w[sa[i]]);
  67. for(int i=;i<=n;i++)
  68. ans=max(ans,ht[p[i]]+merge(find(p[i]-),find(p[i])));
  69. cout<<ans<<endl;
  70. return ;
  71. }

谢特——后缀数组+tire 树的更多相关文章

  1. 中文分词系列(一) 双数组Tire树(DART)详解

    1 双数组Tire树简介 双数组Tire树是Tire树的升级版,Tire取自英文Retrieval中的一部分,即检索树,又称作字典树或者键树.下面简单介绍一下Tire树. 1.1 Tire树 Trie ...

  2. 中文分词系列(二) 基于双数组Tire树的AC自动机

    秉着能偷懒就偷懒的精神,关于AC自动机本来不想看的,但是HanLp的源码中用户自定义词典的识别是用的AC自动机实现的.唉-没办法,还是看看吧 AC自动机理论 Aho Corasick自动机,简称AC自 ...

  3. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

  4. 【XSY1551】往事 广义后缀数组 线段树合并

    题目大意 给你一颗trie树,令\(s_i\)为点\(i\)到根的路径上的字符组成的字符串.求\(max_{u\neq v}(LCP(s_u,s_v)+LCS(s_u,s_v))\) \(LCP=\) ...

  5. BZOJ3473:字符串(后缀数组,主席树,二分,ST表)

    Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 一 ...

  6. BZOJ 2865 字符串识别 | 后缀数组 线段树

    集训讲字符串的时候我唯一想出正解的题-- 链接 BZOJ 2865 题面 给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的.最短的.在原串中只出现过一次的子串. 题解 ...

  7. [BZOJ4556][Tjoi2016&Heoi2016]字符串 后缀数组+主席树

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MB Description 佳媛姐姐过生日的时候,她的小 ...

  8. LOJ_#2720. 「NOI2018」你的名字 _后缀数组+主席树+倍增

    题面: https://loj.ac/problem/2720 考虑枚举T串的每个后缀i,我们要做两件事. 一.统计有多少子串[i,j]在S中要求位置出现. 二.去重. 第二步好做,相当于在后缀数组上 ...

  9. P5346 【XR-1】柯南家族(后缀数组+主席树)

    题目 P5346 [XR-1]柯南家族 做法 聪明性是具有传递性的,且排列是固定的 那么先预处理出每个点的名次,用主席树维护\(k\)大值 一眼平衡树,遍历的同时插入\(O(log^2n)\),总时间 ...

随机推荐

  1. 代码题(1)—lower_bound和upper_bound算法

    1.lower_bound:查找序列中的第一个出现的值大于等于val的位置 这个序列中可能会有很多重复的元素,也可能所有的元素都相同,为了充分考虑这种边界条件,STL中的lower_bound算法总体 ...

  2. json与jsonp详解

     前言: 说到AJAX就会不可避免的面临两个问题,第一个是AJAX以何种格式来交换数据?第二个是跨域的需求如何解决?这两个问题目前都有不同的解决方案,比如数据可以用自定义字符串或者用XML来描述,跨域 ...

  3. php 代码中的箭头“ ->”与“=>”是什么意思?

    类是一个复杂数据类型,这个类型的数据主要有属性.方法两种东西. 属性其实是一些变量,可以存放数据,存放的数据可以是整数.字符串,也可以是数组,甚至是类. 方法实际上是一些函数,用来完成某些功能. 引用 ...

  4. inux命令学习笔记(13):less 命令

    less 工具也是对文件或其它输出进行分页显示的工具,应该说是linux正统查看文件内容的工具,功能极其强大. less 的用法比起 more 更加的有弹性.在 more 的时候,我们并没有办法向前面 ...

  5. 【集成学习】lightgbm参数介绍(sklearn)

    #  XGBoost和LightGBM部分参数对比表: lightgbm.sklearn参数介绍(官网)

  6. APIO2017 游记

    参加了APIO的同学肯定知道我为什么只写标题不写内容. QAQ

  7. 洛谷【P1619】 解一元二次方程的烦恼

    我对模拟的理解:https://www.cnblogs.com/AKMer/p/9064018.html 题目传送门:https://www.luogu.org/problemnew/show/P16 ...

  8. POJ2080:Calendar(计算日期)

    Calendar Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 12842   Accepted: 4641 Descrip ...

  9. HDOJ1251(前缀匹配---分块查找&map应用)

    分块查找算法 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm ...

  10. java流类

    总结:new FileInputStream package com.ds; import java.io.*; import com.da.fgbv; public class rter { pub ...