luogu P5287 [HNOI2019]JOJO
神™这题暴力能A,这出题人都没造那种我考场就想到的数据,难怪我的垃圾做法有分
先考虑没有撤销操作怎么做,因为每次插入一段一样的字符,所以我们可以把\(x\)个字符\(c\)定义为\(cx\),然后用这种新字符做\(\mathrm{kmp}\).但是直接把一般的\(\mathrm{kmp}\)搬过来做是错的.例如\(yybbbyybb\),最后一个b的\(next\)是第二个b,但是题目有个限制,每次往后加的字符不会和上一个字符相同,那么现在往后加任何字符,因为都不等于b,所以就一定无法继续匹配.所以,新的\(\mathrm{kmp}\)的\(next\)指向的字符必须和当前字符的长度和字符类型要一致,这样才能继续往后接东西.还有一种特殊情况,如果当前字符的类型和第一个相同,但是长度比第一个长,那么\(next\)应该指向1,因为这样也是可以往后接东西的
然后考虑统计答案,因为这个\(\mathrm{kmp}\)可能会跳过一些匹配,所以我们在暴跳\(next\)的过程中顺便统计答案,就是如果\(next\)后面的字符类型和当前相同,答案要加上 当前字符没算过答案的部分 到那两个字符长度最小值的这个区间 在\(next\)后面的字符中的到开始位置的长度之和(请感性理解)
然后有撤销操作,直接上个可持久化我们发现可以把所有操作的串建一个trie树,然后在上面dfs做,然后撤销,就可以不用可持久化了qwq
我不可持久化啦!JOJO!
这样就能获得100分的好成绩(误
然而\(\mathrm{kmp}\)是均摊\(O(n)\)的,出题人想卡你还是可以卡的.所以我们考虑一种叫\(\mathrm{kmp}\)自动机的东西,就是每个状态的后继状态表示这个状态后加一个字符,它的\(next\)会指向哪里,每次转移可以直接把\(next\)设成对应的后继,然后在把对应的后继状态指向当前位置.现在因为新的字符集比较大,所以可以使用可持久化线段树维护这个\(\mathrm{kmp}\)自动机.但是因为现在是一步跳到\(next\),所以考虑如何统计答案.我们可以手玩统计答案过程,假设串是\(...bbb...bb...\),然后我们会先算上前两个b在\(bb\)处的长度,然后加上第三个b在\(bbb\)处的长度,如果从前往后看,可以发现这是每次把一个前缀修改成一个元素值更大的等差数列(公差为1),然后答案只要前缀求和,所以这个东西用可持久化线段树,前缀赋值等差数列以及前缀求和来实现
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
using namespace std;
const int N=1e5+10,M=2e4,mod=998244353;
int rd()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int q,n,to[N*100],ch[N*100][2],rt[N][26],t1;
void inst(int o1,int o2,int x,int y)
{
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(x<=mid)
{
ch[o1][0]=++t1,ch[o1][1]=ch[o2][1];
o1=ch[o1][0],o2=ch[o2][0];
r=mid;
}
else
{
ch[o1][0]=ch[o2][0],ch[o1][1]=++t1;
o1=ch[o1][1],o2=ch[o2][1];
l=mid+1;
}
}
to[o1]=y;
}
int q1(int o,int x)
{
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(x<=mid) o=ch[o][0],r=mid;
else o=ch[o][1],l=mid+1;
}
return to[o];
}
int s[N*100],tg[N*100],ch2[N*100][2],r2[N][26],t2;
int gsm(LL x,LL y){return (1ll*(x+x+y-1)*y/2)%mod;}
void psdn(int o,int l,int r)
{
if(l<r&&tg[o])
{
int mid=(l+r)>>1;
++t2,ch2[t2][0]=ch2[ch2[o][0]][0],ch2[t2][1]=ch2[ch2[o][0]][1],ch2[o][0]=t2,s[ch2[o][0]]=gsm(tg[ch2[o][0]]=tg[o],mid-l+1);
++t2,ch2[t2][0]=ch2[ch2[o][1]][0],ch2[t2][1]=ch2[ch2[o][1]][1],ch2[o][1]=t2,s[ch2[o][1]]=gsm(tg[ch2[o][1]]=tg[o]+mid-l+1,r-mid);
tg[o]=0;
}
}
void modif(int &o,int l,int r,int ll,int rr,LL x)
{
psdn(o,l,r);
int lc=ch2[o][0],rc=ch2[o][1];
++t2,s[t2]=gsm((x+max(l-ll,0))%mod,min(r,rr)-max(l,ll)+1),o=t2;
if(ll<=l&&r<=rr){tg[o]=(x+max(l-ll,0))%mod;return;}
int mid=(l+r)>>1;
ch2[o][0]=lc;
if(ll<=mid) modif(ch2[o][0],l,mid,ll,rr,x);
ch2[o][1]=rc;
if(rr>mid) modif(ch2[o][1],mid+1,r,ll,rr,x);
s[o]=(s[ch2[o][0]]+s[ch2[o][1]])%mod;
}
int quer(int o,int l,int r,int ll,int rr)
{
if(!o) return 0;
psdn(o,l,r);
if(ll<=l&&r<=rr) return s[o];
int mid=(l+r)>>1,an=0;
if(ll<=mid) an+=quer(ch2[o][0],l,mid,ll,rr);
if(rr>mid) an+=quer(ch2[o][1],mid+1,r,ll,rr);
return an%mod;
}
int an[N],sg[26],p[N],nw,aa[N],t0;
vector<int> e[N];
int sta[N],mx[N][26],tp;
LL len[N];
void wk(int x)
{
sta[++tp]=aa[x];
int xx=aa[x]/M,ln=aa[x]%M;
len[tp]=(len[tp-1]+ln)%mod;
int nxt=0;
if(tp==1) an[x]=gsm(1,ln-1);
else
{
nxt=q1(rt[tp][xx],ln);
an[x]=(an[x]+quer(r2[tp][xx],1,n,1,ln))%mod;
if(!nxt&&sta[1]/M==xx&&sta[1]%M<ln) nxt=1,an[x]=(an[x]+1ll*len[1]*max(ln-mx[tp][xx],0)%mod)%mod;
}
int las=rt[tp][xx];
inst(rt[tp][xx]=++t1,las,ln,tp);
modif(r2[tp][xx],1,n,1,ln,len[tp-1]+1);
mx[tp][xx]=max(mx[tp][xx],ln);
int nn=e[x].size();
for(int i=0;i<nn;++i)
{
an[e[x][i]]=an[x];
memcpy(rt[tp+1],rt[nxt+1],sizeof(int)*26),memcpy(r2[tp+1],r2[nxt+1],sizeof(int)*26),memcpy(mx[tp+1],mx[nxt+1],sizeof(int)*26);
wk(e[x][i]);
}
--tp;
}
int main()
{
//awsl
q=rd();
char cc[4];
for(int i=1;i<=q;++i)
{
int op=rd(),x=rd();
if(op==1)
{
n=max(n,x);
scanf("%s",cc);
p[i]=++t0;
e[nw].push_back(t0),aa[t0]=(cc[0]-'a')*M+x,nw=t0;
}
else nw=p[i]=p[x];
}
int nn=e[0].size();
for(int i=0;i<nn;++i)
memset(rt[1],0,sizeof(int)*26),memset(r2[1],0,sizeof(int)*26),memset(mx[1],0,sizeof(int)*26),wk(e[0][i]);
for(int i=1;i<=q;++i) printf("%d\n",an[p[i]]);
return 0;
}
luogu P5287 [HNOI2019]JOJO的更多相关文章
- HNOI2019 JOJO
HNOI2019 JOJO jojo这个坑填上了,然鹅还有序列这个题啊啊啊啊啊啊 膜 可持久化这个东西没有强制在线就是假的,直接建树dfs就行了 这题是kmp的加强版,每次会加一堆相同的数进来 先想一 ...
- Luogu P5292 [HNOI2019]校园旅行
非常妙的一道思博题啊,不愧是myy出的题 首先我们考虑一个暴力DP,直接开一个数组\(f_{i,j}\)表示\(i\to j\)的路径能否构成回文串 考虑直接拿一个队列来转移,队列里存的都是\(f_{ ...
- luogu P5286 [HNOI2019]鱼
传送门 这题真的牛皮,还好考场没去刚( 这题口胡起来真的简单 首先枚举D点,然后对其他所有点按极角排序,同时记录到D的距离.然后按照极角序枚举A,那么鱼尾的两个点的极角范围就是A关于D对称的那个向量, ...
- luogu P5288 [HNOI2019]多边形
传送门 这是什么神仙操作... 首先要注意一些性质.首先每一个\((x,n)\)的边可以把当前多边形分成两半,这两半的操作是独立的.然后对于某一个没有\((x,n)\)的边的多边形,最优操作是唯一的. ...
- luogu P5293 [HNOI2019]白兔之舞
传送门 关于这题答案,因为在所有行,往后跳到任意一行的\(w_{i,j}\)都是一样的,所以可以算出跳\(x\)步的答案然后乘上\(\binom{l}{x}\),也就是枚举跳到了哪些行 如果记跳x步的 ...
- luogu P5294 [HNOI2019]序列
传送门 这个什么鬼证明直接看uoj的题解吧根本不会证明 首先方案一定是若干段等值的\(B\),然后对于一段,\(B\)的值应该是\(A\)的平均值.这个最优方案是可以线性构造的,也就是维护以区间平均值 ...
- 【洛谷5287】[HNOI2019] JOJO(主席树优化KMP)
点此看题面 大致题意: 每次往一个字符串末尾加上\(x\)个字符\(c\),或者回到某一历史版本,求\(KMP\)的\(\sum Next_i\). 问题转化 考虑到可以离线. 于是,我们就可以用一个 ...
- 【题解】Luogu P5288 [HNOI2019]多边形
原题传送门 HN的题目就是毒瘤 我们有以下猜想: 1.最后所有的线都连到了n号点上 2.最小步数应该为n-3-已经连到n号点的线段数量 本来有些边\((a_i,n)\)会将整个图分割成很多个区间.对于 ...
- 【题解】Luogu P5294 [HNOI2019]序列
原题传送门 题意:给你一个长度为\(n\)的序列\(A\),每次询问修改一个元素(只对当前询问有效),然后让你找到一个不下降序列\(B\),使得这两个序列相应位置之差的平方和最小,并输出这个最小平方和 ...
随机推荐
- vue 首页问题
(现在其实处于不知道自己不知道状态,前端其实很多东东,不信弄个微博试试,还有那些概念的to thi tha) 1.压缩 一般 vue-cli已经压缩了 比如js 的,一般4M多压缩到 1M,还有css ...
- day23--面向对象之封装、继承、多态
面向对象的三大特性: 封装: 在类的内部(class内部)可以由属性和方法,外部代码可以通过直接调用实例变量的方法来操作数据,这样就隐藏了内部的逻辑,但是外部还是可以直接修改实例的属性,因此当需求中存 ...
- C++笔记--std::相关
std::packaged_task https://www.cnblogs.com/haippy/p/3279565.html https://en.cppreference.com/w/cpp/t ...
- 我用Python实现了一个小说网站雏形
前言 前段时间做了一个爬取妹子套图的小功能,小伙伴们似乎很有兴趣,为了还特意组建了一个Python兴趣学习小组,来一起学习.十个python九个爬,在大家的印象中好像Python只能做爬虫.然而并非如 ...
- Java 200+ 面试题补充③ Dubbo 模块
昨天在我的 Java 面试粉丝群里,有一个只有一年开发经验的小伙伴只用了三天时间,就找到了一个年薪 20 万的工作,真是替他感到开心. 他的经历告诉我们:除了加强自我实战经验之外,还要努力积累自己的理 ...
- Eruda 一个被人遗忘的移动端调试神器
引言 日常工作中再牛逼的大佬都不敢说自己的代码是完全没有问题的,既然有问题,那就也就有调试,说到调试工具,大家可能对于fiddler.Charles.chrome devtools.Firebug ...
- Educational Codeforces Round 62 (Rated for Div. 2) - C Playlist
当时题意看错了...不过大致思路是对的,唯一没有想到的就是用优先队列搞这个东西,真是不该啊... 题意大概就是,有N首歌,N首歌有两个东西,一个是长度Ti,一个是美丽值Bi,你最多可以选择K首歌, 这 ...
- 【学习总结】Git学习-参考廖雪峰老师教程-总
公元2018-10-21 实验室台式机 win7 64位 参考教程: 廖雪峰Git教程 其他资料:Git-book 北大一只总结的笔记,最终整理的时候可以参考:Git笔记 评论区看到的另一个人,总结在 ...
- ReentrantLock重入锁详解
1.定义 重入锁:能够支持一个线程对资源的重复加锁,也就是当一个线程获取到锁后,再次获取该锁时而不会被阻塞. 2.可重入锁的应用场景 2.1 如果已经加锁,则不再重复加锁,比如:交互界面点击后响应时间 ...
- fedora上安装ffmpeg
环境 fedora26 1. 安装 yasm sudo dnf install yasm yasm-devel -y 2.安装 ffmpeg 官方下载ffmpeg源码 ( http://ffmpeg ...