[HNOI2019]JOJO(KMP自动机+主席树)

一道神仙题,考察选手对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+10,M=1e4+7,mod=998244353;
struct node{int lc,rc,sum,lazy,nxt;}tr[N*60];
int n,tot,top,val[N],pos[N],ans[N],a[N],b[N],rt[N][26],mx[N][26];
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+1)/2%mod;}
void pushdown(int l,int r,int rt)
{
    if(!tr[rt].lazy)return;
    int mid=l+r>>1;
    newnode(tr[rt].lc),add(tr[rt].lc,tr[rt].lazy,mid-l+1);
    newnode(tr[rt].rc),add(tr[rt].rc,tr[rt].lazy,r-mid);
    tr[rt].lazy=0;
}
void update(int k,int v,int p,int l,int r,int&rt)
{
    newnode(rt);
    if(r<k){add(rt,v,r-l+1);return;}
    if(l==r){tr[rt].nxt=p,add(rt,v,1);return;}
    pushdown(l,r,rt);
    int mid=l+r>>1;
    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>>1;
    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=0;
    a[top]=val[u],b[top]=b[top-1]+y;
    if(top==1)ans[u]=S(y-1);
    else{
        ans[u]=(ans[u]+S(min(mx[top][x],y)))%mod;
        query(y,ans[u],nxt,1,M,rt[top][x]);
        if(!nxt&&a[1]/M==x&&b[1]<y)nxt=1,ans[u]=(ans[u]+1ll*b[1]*max(0,y-mx[top][x]))%mod;
    }
    mx[top][x]=max(mx[top][x],y);
    update(y,b[top-1],top,1,M,rt[top][x]);
    for(int i=0;i<G[u].size();i++)
    {
        memcpy(mx[top+1],mx[nxt+1],sizeof mx[top+1]);
        memcpy(rt[top+1],rt[nxt+1],sizeof rt[top+1]);
        ans[G[u][i]]=ans[u],dfs(G[u][i]);
    }
    --top;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int op,x;
        scanf("%d%d",&op,&x);
        if(op==1)
        {
            char c;cin>>c;
            val[++tot]=(c-'a')*M+x,pos[i]=tot,G[pos[i-1]].push_back(pos[i]);
        }
        else pos[i]=pos[x];
    }
    for(int i=0;i<G[0].size();i++)
    {
        tot=0;
        memset(rt[1],0,sizeof rt[1]);
        memset(mx[1],0,sizeof mx[1]);
        dfs(G[0][i]);
    }
    for(int i=1;i<=n;i++)printf("%d\n",ans[pos[i]]);
}
View Code

 

转载于:https://www.cnblogs.com/hfctf0210/p/10801699.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值