• P5287-[HNOI2019]JOJO【KMP】


    正题

    题目链接:https://www.luogu.com.cn/problem/P5287


    题目大意

    开始一个空串,(n)个操作

    1. 在末尾加入(x)(c)字符(保证和(c)和前面的字符不同)
    2. 返回到第(x)次操作之后

    每次操作完成后求所有前缀的最长的(border)长度和

    (1leq nleq 10^5)


    解题思路

    二操作好像是一个离线树能搞出来的先只考虑一操作,因为是相当于求(kmp)之后的(next)和,所以可以考虑一下用(kmp)

    每个插入操作我们可以看做插入了一个二元组((c,x))(nxt_i)表示按照二元组完全匹配来KMP的(next)数组。

    然后求答案的时候设现在这个二元组((c,x)),已经匹配到(now)(c),然后往前跳(nxt)的时候如果下一个二元组的字符是(c)且数量(x>now)那么我们就往后计算答案然后让(now=x)

    然后最后剩下的一些如果能和第一个匹配就全都匹配。

    然后这样跑是能过的,但是加了二操作之后就会(T),因为KMP是均摊复杂度的,会被(hack)

    所以考虑一下怎么优化,我们知道(broder)可以被划分成(log)个等差数列,而且一定是按照长度来排列的,如果我们发现我们跳进了一个无法匹配的等差数列中我们就直接跳到这个等差数列结束的位置因为这个等差数列中已经不能匹配了。

    这样复杂度就到了(O(nlog n))


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #define ll long long
    using namespace std;
    const ll N=1e5+10,P=998244353;
    ll n,len,ch[N][2],s[N][3],ans[N],nxt[N],prt[N],p[N];
    vector<ll> e[N];
    ll gs(ll l,ll r)
    {return (r+l)*(r-l+1)/2;}
    void dfs(ll x){
    	ll i=nxt[len++];
    	s[len][0]=ch[x][0];
    	s[len][1]=ch[x][1];
    	s[len][2]=s[len-1][2]+ch[x][1];
    	ans[len]=ans[len-1];nxt[len]=0;
    	if(len==1){ans[len]=gs(1,ch[x][1]-1);}
    	else{
    		ll d=len-i;
    		while(i&&(s[i+1][0]!=ch[x][0]||s[i+1][1]!=ch[x][1])){
    			if(i-nxt[i]==d)i=i%d+d;
    			d=i-nxt[i];i=nxt[i];
    		}
    		nxt[len]=i+(i||(ch[x][0]==s[1][0]&&ch[x][1]>=s[1][1]));
    		ll now=0;i=nxt[len-1],d=len-1-i;
    		while(now<ch[x][1]&&i){
    			if(s[i+1][0]==ch[x][0]&&s[i+1][1]>now){
    				ans[len]+=gs(s[i][2]+now+1,s[i][2]+min(ch[x][1],s[i+1][1]));
    				now=s[i+1][1];
    			}
    			if(i-nxt[i]==d)i=i%d+d;
    			d=i-nxt[i];i=nxt[i];
    		}
    		if(now<ch[x][1]&&s[1][0]==ch[x][0]){
    			if(s[i+1][1]>now)ans[len]+=gs(now+1,min(ch[x][1],s[i+1][1]));
    			now=max(now,min(ch[x][1],s[i+1][1]));
    			ans[len]+=s[1][1]*(ch[x][1]-now);
    		}
    	}
    	prt[x]=ans[len];
    	for(ll i=0;i<e[x].size();i++)
    		dfs(e[x][i]);
    	len--;return;
    }
    signed main()
    {
    	scanf("%lld",&n);
    	for(ll i=1;i<=n;i++){
    		ll op;scanf("%lld ",&op);
    		if(op==1){
    			scanf("%lld %c",&ch[i][1],&ch[i][0]);
    			e[p[i-1]].push_back(p[i]=i);
    		}
    		else{
    			ll x;scanf("%lld",&x);
    			p[i]=p[x];
    		}
    	}
    	for(ll i=0;i<e[0].size();i++)
    		dfs(e[0][i]);
    	for(ll i=1;i<=n;i++)
    		printf("%lld
    ",prt[p[i]]%P);
    	return 0;
    }
    
  • 相关阅读:
    2011年需要关注的9大编程语言 狼人:
    微软在华推广Win7拒绝“黑屏” 狼人:
    ifanr访谈:GuruDigger — Web工程师排排坐 狼人:
    10种破除网页设计师障碍的实用方法 狼人:
    英特尔CEO:微软Windows 7是PC更新的催化剂 狼人:
    Windows 7市场份额突破25% XP持续下滑 狼人:
    Office Web Apps中文版正式上线 狼人:
    机器学习实现线性梯度算实现octave
    管理系统数据库sql server 数据库管理
    缓存代码sencha Touch 缓存问题解析
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14912382.html
Copyright © 2020-2023  润新知