点此看题面

大致题意: 每次往一个字符串末尾加上\(x\)个字符\(c\),或者回到某一历史版本,求\(KMP\)的\(\sum Next_i\)。

问题转化

考虑到可以离线

于是,我们就可以用一个比较常用的技巧,从每个版本向由其转移到的版本连边,然后从\(0\)的各个子节点出发遍历一遍操作树,且每操作完一个节点的子树就清空当前贡献。

则我们就只用考虑如何计算每次新加入的一段字符对答案的影响即可,问题一下简单了许多。

暴力

接下来我们考虑如何计算新加入的一段字符对答案的影响,首先思考如何暴力。

可以暴力跳\(KMP\)。

假设我们加入了\(x\)个字符\(c\),则我们跳\(Next\)数组来枚举之前每次加入字符\(c\)的操作,假设当前枚举的操作加入了\(y\)个字符\(c\)。

则对于\(x=y\),它们两个恰好可以匹配。

否则,就说明当前加入的前\(min(x,y)\)个字符可以与之前加入的这一段计算答案。

注意,仅限于计算答案,而不能匹配,因为这两段是无法匹配对后续操作造成影响的。

这个算法使用了\(KMP\),看起来是一个很珂学的做法,那么为什么说它是暴力呢?

因为\(KMP\)的复杂度是均摊的,而我们为了方便将其放在操作树上搞,这样复杂度就不正确了,随便被卡。

主席树优化

回顾我们前面提到过的计算答案的方式,就是找到之前某一段长度为\(y\)的字符\(c\),然后前\(min(x,y)\)个字符可以一起算答案。

显然这里每个字符只能算一次答案,且由于\(Next\)数组的定义,我们必须找最靠后的。

则我们对于插入一段\(x\)个字符\(c\),直接在关于\(c\)的主席树上修改\([1,x]\)这段区间值为这次操作前已插入的字符个数,并在第\(x\)个位置上记录下\(Next\)为当前操作。

查询时同样在主席树上以类似方式查询即可。

然后要处理下一个操作时我们就将当前信息(根节点信息等)复制到下一个操作要用的数组,然后就可以继续处理了。

具体实现详见代码。

代码

  1. #include<bits/stdc++.h>
  2. #define Tp template<typename Ty>
  3. #define Ts template<typename Ty,typename... Ar>
  4. #define Reg register
  5. #define RI Reg int
  6. #define Con const
  7. #define CI Con int&
  8. #define I inline
  9. #define W while
  10. #define N 100000
  11. #define M 10000
  12. #define Log 20
  13. #define X 998244353
  14. #define Gmax(x,y) (x<(y)&&(x=(y)))
  15. #define min(x,y) ((x)<(y)?(x):(y))
  16. #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
  17. #define Memset(x,y) memset(x,y,sizeof(x))
  18. #define Memcpy(x,y) memcpy(x,y,sizeof(x))
  19. #define Inc(x,y) ((x+=(y))>=X&&(x-=X))
  20. #define Dec(x,y) ((x-=(y))<0&&(x+=X))
  21. #define XSum(x,y) ((x)+(y)>=X?(x)+(y)-X:(x)+(y))
  22. #define XSub(x,y) ((x)<(y)?(x)-(y)+X:(x)-(y))
  23. using namespace std;
  24. int n,ee,a[N+5],b[N+5],p[N+5],lnk[N+5];struct edge {int to,nxt;}e[N+5];
  25. class FastIO
  26. {
  27. private:
  28. #define FS 100000
  29. #define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
  30. #define pc(c) (C^FS?FO[C++]=c:(fwrite(FO,1,C,stdout),FO[(C=0)++]=c))
  31. #define tn (x<<3)+(x<<1)
  32. #define D isdigit(c=tc())
  33. int T,C;char c,*A,*B,FI[FS],FO[FS],S[FS];
  34. public:
  35. I FastIO() {A=B=FI;}
  36. Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
  37. Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
  38. Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
  39. Tp I void writeln(Con Ty& x) {write(x),pc('\n');}
  40. I void readc(char& x) {W(isspace(x=tc()));}
  41. I void clear() {fwrite(FO,1,C,stdout),C=0;}
  42. #undef D
  43. }F;
  44. class OperatorTreeDfser//操作树上DFS
  45. {
  46. private:
  47. #define Sum(x) ((1LL*(x)*((x)+1)>>1)%X)
  48. int a1,b1,T,s[N+5],rt[N+5][30],Mx[N+5][30],ans[N+5];
  49. class ChairmanTree//主席树
  50. {
  51. private:
  52. #define L l,mid,O[rt].S[0]
  53. #define R mid+1,r,O[rt].S[1]
  54. #define PU(x) (O[x].Su=XSum(O[O[x].S[0]].Su,O[O[x].S[1]].Su))
  55. #define PD(x) O[x].F&&\
  56. (\
  57. O[++tot]=O[O[x].S[0]],O[x].S[0]=tot,U(O[x].S[0],mid-l+1,O[x].F),\
  58. O[++tot]=O[O[x].S[1]],O[x].S[1]=tot,U(O[x].S[1],r-mid,O[x].F),\
  59. O[x].F=0\
  60. )//下传标记
  61. #define U(x,s,v) (O[x].Su=1LL*(s)*(v)%X,O[x].F=v)
  62. int tot;struct node {int Nxt,Su,F,S[2];}O[N*Log*3+5];
  63. I void upt(CI l,CI r,int& rt,CI x,CI v,CI nxt)//区间修改值,单点修改nxt
  64. {
  65. if(O[++tot]=O[rt],rt=tot,r<x) return (void)U(rt,r-l+1,v);
  66. if(l==r) return (void)(U(rt,1,v),O[rt].Nxt=nxt);RI mid=l+r>>1;PD(rt),
  67. x>mid&&(upt(R,x,v,nxt),0),upt(L,x,v,nxt),PU(rt);
  68. }
  69. I int qry(CI l,CI r,CI rt,CI x,int& sum)//区间询问和,单点询问nxt
  70. {
  71. if(l==r) return Inc(sum,O[rt].Su),O[rt].Nxt;RI mid=l+r>>1;PD(rt);
  72. return x<=mid?qry(L,x,sum):(Inc(sum,O[O[rt].S[0]].Su),qry(R,x,sum));
  73. }
  74. public:
  75. I void Clear() {tot=0;}//清空节点数
  76. I void Update(int& rt,CI x,CI v,CI nxt) {upt(1,M,rt,x,v,nxt);}
  77. I int Query(CI rt,CI x,int& sum) {return qry(1,M,rt,x,sum);}
  78. }C;
  79. public:
  80. I void Clear() {s[1]=0,C.Clear(),Memset(rt,0),Memset(Mx,0);}//清空信息
  81. I int GetAns(CI x) {return ans[x];}//求对应的答案
  82. I void dfs(CI x)//操作树上DFS
  83. {
  84. RI i,v=a[x],u=b[x],nxt=0;++T^1?//如果不是第一次操作
  85. (
  86. Inc(ans[x],Sum(min(v,Mx[T][u]))),nxt=C.Query(rt[T][u],v,ans[x]),//询问信息
  87. !nxt&&a1<v&&b1==u&&(nxt=1,v>Mx[T][u]&&Inc(ans[x],1LL*a1*(v-Mx[T][u])%X))//处理特殊情况
  88. ):(a1=v,b1=u,ans[x]=Sum(v-1));//第一次操作特殊处理
  89. Gmax(Mx[T][u],v),s[T]+=v,C.Update(rt[T][u],v,s[T-1],T);//修改
  90. for(i=lnk[x];i;i=e[i].nxt)//枚举儿子遍历
  91. Memcpy(rt[T+1],rt[nxt+1]),Memcpy(Mx[T+1],Mx[nxt+1]),//复制信息
  92. ans[e[i].to]=ans[x],s[T+1]=s[T],dfs(e[i].to);//复制信息,然后处理
  93. --T;//弹去这一次操作
  94. }
  95. }D;
  96. int main()
  97. {
  98. RI i,op,x,t=0;char c;for(F.read(n),i=1;i<=n;++i)
  99. F.read(op,x),op^2?(a[p[i]=++t]=x,F.readc(c),b[t]=c&31,add(p[i-1],p[i])):(p[i]=p[x]);//读入+建边
  100. for(i=lnk[0];i;i=e[i].nxt) D.Clear(),D.dfs(e[i].to);//从0的每个儿子出发开始遍历
  101. for(i=1;i<=n;++i) F.writeln(D.GetAns(p[i]));return F.clear(),0;//输出答案
  102. }

【洛谷5287】[HNOI2019] JOJO(主席树优化KMP)的更多相关文章

  1. 洛谷P2617 Dynamic Rankings (主席树)

    洛谷P2617 Dynamic Rankings 题目描述 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a ...

  2. 洛谷P3567 KUR-Couriers [POI2014] 主席树/莫队

    正解:主席树/莫队 解题报告: 传送门! 这题好像就是个主席树板子题的样子,,,? 毕竟,主席树的最基本的功能就是,维护一段区间内某个数字的个数 但是毕竟是刚get到主席树,然后之前做的一直是第k大, ...

  3. 洛谷P3567[POI2014]KUR-Couriers(主席树+二分)

    题意:给一个数列,每次询问一个区间内有没有一个数出现次数超过一半 题解: 最近比赛太多,都没时间切水题了,刚好日推了道主席树裸题,就写了一下 然后 WA80 WA80 WA0 WA90 WA80 ?? ...

  4. 洛谷P3567 [POI2014]KUR-Couriers 主席树

    挺裸的,没啥可讲的. 不带修改的主席树裸题 Code: #include<cstdio> #include<algorithm> using namespace std; co ...

  5. 洛谷$P3302$ 森林 $[SDOI2013]$ 主席树

    正解:主席树 解题报告: 传送门! 口胡一时爽代码火葬场 这题想法不难,,,但显然的是代码应该还挺难打的 但反正我也不放代码,就写下题解趴$QwQ$ 第一问就是个$Count\ on\ a\ tree ...

  6. 洛谷P4602 [CTSC2018]混合果汁(主席树)

    题目描述 小 R 热衷于做黑暗料理,尤其是混合果汁. 商店里有 nn 种果汁,编号为 0,1,\cdots,n-10,1,⋯,n−1 . ii 号果汁的美味度是 d_idi​ ,每升价格为 p_ipi ...

  7. 洛谷P2617 Dynamic Rankings 主席树 单点修改 区间查询第 K 大

    我们将线段树套在树状数组上,查询前预处理出所有要一起移动的节点编号,并在查询过程中一起将这些节点移到左右子树上. Code: #include<cstdio> #include<cs ...

  8. 洛谷4137 mex题解 主席树

    题目链接 虽然可以用离线算法水过去,但如果强制在线不就gg了. 所以要用在线算法. 首先,所有大于n的数其实可以忽略,因为mex的值不可能大于n 我们来设想一下,假设已经求出了从0到n中所有数在原序列 ...

  9. 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释

    P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...

随机推荐

  1. PIE SDK灾前灾后对比

    灾前灾后对比功能是GIS软件中常用的功能之一,指利用多时相获取的覆盖同一地表区域的遥感影像及其它辅助数据来确定和分析地表变化.它利用计算机图像处理系统,对不同时段目标或现象状态的变化进行识别.分析:它 ...

  2. c语言实现队列的基本操作

    话不多说,直接代码 #include"stdio.h" #include"stdlib.h" typedef struct QNode{ int date; s ...

  3. Oracle 系统常用命令

    1.基本口令 1.1.show user                               作用:显示当前连接用户 1.2.conn 用户名/密码                       ...

  4. Android IntentFilter 匹配原则浅析

    1 Intent分为两大类,显式和隐式. 显式事件,就是指通过 component Name 属性,明确指定了目标组件的事件. 比如我们新建一个Intent,指名道姓的说,此事件用于启动名为" ...

  5. Java Swing笔记

    想到了解一下GUI主要是想用来做点小工具,记录一些笔记. 文本框自动换行和滚动条 private static JTextArea addJTextArea(JPanel panel, int x, ...

  6. C#的params参数遇到null

    params参数支持数组作为参数传入,但并不支持List 定义一个使用params的参数 private static void UseParam(params int[] args) { if (a ...

  7. centos7 中文乱码问题解决方法

    1.查看是否安装中文包 可以使用下面的命名查看系统是否安装了中文安装包. locale -a |grep "zh_CN" 没有输出,说明没有安装,输入下面的命令安装: yum gr ...

  8. nyoj 1239——引水工程——————【最小生成树 prim】

    引水工程 时间限制:2000 ms  |  内存限制:65535 KB 难度:3   描述 南水北调工程是优化水资源配置.促进区域协调发展的基础性工程,是新中国成立以来投资额最大.涉及面最广的战略性工 ...

  9. C#生成二维码的内容

    生成二维码的内容 using QRCoder; // 生成二维码的内容 string strCode = this.txtQr.Text.Trim(); if (string.IsNullOrWhit ...

  10. win 环境下 node.js环境变量

     在win 环境下 node.js环境变量有两种情况:  (1)开发环境(development):开发环境是程序猿们专门用于开发的服务器,配置可以比较随意, 为了开发调试方便,一般打开全部错误报告. ...