一道神仙题,考察选手对KMP的深入理解。

先考虑没有2操作的做法。设每一段为一个二元组(x,c),考虑一段前缀匹配后缀,除了第一段的字符,其他段的二元组(x,c)必须相等,所以可以将其视为特殊字符进行匹配。在串末尾加入(x,c)时,显然不断跳next数组,如果当前前缀后接的字符为c ,那么可以增加一段首项为当前前缀长度,然后发现这一段的next数组为首项为前缀长度,公差为1的等差数列。next链上如果有等于(x,c)的二元组,则next指向二元组,否则指向0,因为如果存在(y,c)满足y>x,则一定无法匹配。

解决了没有2操作的做法,考虑有2操作,由于KMP复杂度是均摊的,很显然不能直接在树上dfs,所以要优化求next过程。考虑离线建树,dfs时维护每条next链上的KMP自动机,f[i][j][k]表示状态i下加入二元组(j,k)next指向哪,g[i][j][k]表示产生的贡献,每次修改复制前一次的f数组,修改f[i][x][c],g[i][x][1...c]为等差数列,此处可以用主席树实现。

#include<bits/stdc++.h>
#define lson l,mid,tr[rt].lc
#define rson mid+1,r,tr[rt].rc
using namespace std;
const int N=1e5+,M=1e4+,mod=;
struct node{int lc,rc,sum,lazy,nxt;}tr[N*];
int n,tot,top,val[N],pos[N],ans[N],a[N],b[N],rt[N][],mx[N][];
vector<int>G[N];
void newnode(int&x){tr[++tot]=tr[x],x=tot;}
void add(int x,int v,int len){tr[x].sum=1ll*v*len%mod,tr[x].lazy=v;}
int S(int x){return 1ll*x*(x+)/%mod;}
void pushdown(int l,int r,int rt)
{
if(!tr[rt].lazy)return;
int mid=l+r>>;
newnode(tr[rt].lc),add(tr[rt].lc,tr[rt].lazy,mid-l+);
newnode(tr[rt].rc),add(tr[rt].rc,tr[rt].lazy,r-mid);
tr[rt].lazy=;
}
void update(int k,int v,int p,int l,int r,int&rt)
{
newnode(rt);
if(r<k){add(rt,v,r-l+);return;}
if(l==r){tr[rt].nxt=p,add(rt,v,);return;}
pushdown(l,r,rt);
int mid=l+r>>;
update(k,v,p,lson);
if(k>mid)update(k,v,p,rson);
tr[rt].sum=(tr[tr[rt].lc].sum+tr[tr[rt].rc].sum)%mod;
}
void query(int k,int&ans,int&nxt,int l,int r,int&rt)
{
if(r<k){ans=(ans+tr[rt].sum)%mod;return;}
if(l==r){ans=(ans+tr[rt].sum)%mod,nxt=tr[rt].nxt;return;}
pushdown(l,r,rt);
int mid=l+r>>;
query(k,ans,nxt,lson);
if(k>mid)query(k,ans,nxt,rson);
}
void dfs(int u)
{
++top;
int x=val[u]/M,y=val[u]%M,nxt=;
a[top]=val[u],b[top]=b[top-]+y;
if(top==)ans[u]=S(y-);
else{
ans[u]=(ans[u]+S(min(mx[top][x],y)))%mod;
query(y,ans[u],nxt,,M,rt[top][x]);
if(!nxt&&a[]/M==x&&b[]<y)nxt=,ans[u]=(ans[u]+1ll*b[]*max(,y-mx[top][x]))%mod;
}
mx[top][x]=max(mx[top][x],y);
update(y,b[top-],top,,M,rt[top][x]);
for(int i=;i<G[u].size();i++)
{
memcpy(mx[top+],mx[nxt+],sizeof mx[top+]);
memcpy(rt[top+],rt[nxt+],sizeof rt[top+]);
ans[G[u][i]]=ans[u],dfs(G[u][i]);
}
--top;
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
{
int op,x;
scanf("%d%d",&op,&x);
if(op==)
{
char c;cin>>c;
val[++tot]=(c-'a')*M+x,pos[i]=tot,G[pos[i-]].push_back(pos[i]);
}
else pos[i]=pos[x];
}
for(int i=;i<G[].size();i++)
{
tot=;
memset(rt[],,sizeof rt[]);
memset(mx[],,sizeof mx[]);
dfs(G[][i]);
}
for(int i=;i<=n;i++)printf("%d\n",ans[pos[i]]);
}

[HNOI2019]JOJO(KMP自动机+主席树)的更多相关文章

  1. LOJ 3055 「HNOI2019」JOJO—— kmp自动机+主席树

    题目:https://loj.ac/problem/3055 先写了暴力.本来想的是 n<=300 的那个在树上暴力维护好整个字符串, x=1 的那个用主席树维护好字符串和 nxt 数组.但 x ...

  2. NOI 2011 阿狸的打字机(AC自动机+主席树)

    题意 https://loj.ac/problem/2444 思路 ​多串匹配,考虑 \(\text{AC}\) 自动机.模拟打字的过程,先建出一棵 \(\text{Trie}\) 树,把它变成自动机 ...

  3. Bzoj2534:后缀自动机 主席树启发式合并

    国际惯例的题面:考虑我们求解出字符串uvu第一个u的右端点为i,第二个u的右端点为j,我们需要满足什么性质?显然j>i+L,因为我们选择的串不能是空串.另外考虑i和j的最长公共前缀(也就是说其p ...

  4. luogu P5826 【模板】子序列自动机 主席树 vector 二分

    LINK:子序列自动机 想了一些很有趣的做法. dp 容易看出 f[i][j]表示前i个数匹配了j个数的dp 不过复杂度很高. 贪心 容易想到匹配的时候每个数字尽量往前匹配 这样显然是最优的 复杂度Q ...

  5. CodeForces 547E Mike and Friends AC自动机 主席树

    题意: 给出\(n\)个字符串\(s_i\)和\(q\)个询问: \(l,r,k\):\(\sum\limits_{i=l}^{r}count(i, k)\),其中\(count(i,j)\)表示\( ...

  6. 【洛谷5287】[HNOI2019] JOJO(主席树优化KMP)

    点此看题面 大致题意: 每次往一个字符串末尾加上\(x\)个字符\(c\),或者回到某一历史版本,求\(KMP\)的\(\sum Next_i\). 问题转化 考虑到可以离线. 于是,我们就可以用一个 ...

  7. 【BZOJ-4556】字符串 后缀数组+二分+主席树 / 后缀自动机+线段树合并+二分

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 657  Solved: 274[Su ...

  8. CodeForces 547E:Mike and Friends(AC自动机+DFS序+主席树)

    What-The-Fatherland is a strange country! All phone numbers there are strings consisting of lowercas ...

  9. CF547E Milk and Friends(AC自动机的fail指针上建主席树 或 广义后缀自动机的parent线段树合并)

    What-The-Fatherland is a strange country! All phone numbers there are strings consisting of lowercas ...

随机推荐

  1. 吴裕雄--天生自然 JAVASCRIPT开发学习:条件语句

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  2. Ansible常见错误解析

    背景 由于工作中经常用到ansible,所以整理了常用的ansible错误及原因分析,方便自己也方便别人参考. 1.shell 模块常见错误 1.1 使用shell遇到"msg": ...

  3. js事件函数中function(e){}

    简单来说就是指向了当前发生的事件(click.mouseover等等),保存了当前事件的信息.如鼠标点击事件,有鼠标的坐标信息.其中,e是标准浏览器传递进去的事件参数,低版本IE不会传递,事件参数放置 ...

  4. Aspectj切入点语法定义

    例如定义切入点表达式  execution (* com.sample.service.impl..*.*(..)) execution()是最常用的切点函数,其语法如下所示: 整个表达式可以分为五个 ...

  5. JS-语句三

    关于if语句的几个练习: 1. 输入三个整数,x,y,z,最终以从小到大的方式输出.  思路:先列举出每种可能,然后做if套嵌.        var x = prompt("请输入一个数字 ...

  6. Javascript object.constructor属性与面向对象编程(oop)

    定义和用法 在 JavaScript 中, constructor 属性返回对象的构造函数. 返回值是函数的引用,不是函数名: JavaScript 数组 constructor 属性返回 funct ...

  7. Python Learning Day1

    字符串的操作 # 字符串的操作 str1 = 'my name is xxx, my age is 18.' # 优先掌握的操作: # 1.按索引取值(正向取+反向取) :只能取 print(str1 ...

  8. Unity3d游戏代码保护

    现在的游戏项目如果达到一定规模.项目比较创新方竞争对手.项目严重依赖客户端代码那么代码保护还是尽量做,如果不是也没必须瞎折腾. Unity常见代码保护机制: 1.重新编译mono,修改mono_ima ...

  9. Sqlserver 标量函数

    以前只是了解标量函数这个概念,感觉使用量好像并不大,等我真正做sql编码的时候才发现它的好处.简直太方便了实用了. 我们知道在进行软件开发的时候要定义很多不同类型,每个类型又会分很多项.比如: 搞前端 ...

  10. PAT A1001-A1004

    题集通道:https://pintia.cn/problem-sets/994805342720868352/problems/type/7 A1001 :  A+B Format (20 point ...