• P5287 [HNOI2019]JOJO border理论 主席树


    题意:

    戳这里

    分析:

    • 暴力

    直接KMP,复杂度O((n^2))

    • 正解

    首先因为不强制在线,我们可以建出操作树,然后DFS解决操作2的问题,然后我们考虑操作1怎么做,由于每一次暴力KMP的复杂度过高,所以我们要优化KMP。

    我们把压缩的字符看成一个二元组,对于一个前缀,如果他是border,他一定满足除了第一段和最后一段以外,中间所有的二元组都和后缀相同。所以我们考虑加入一个二元组 (x,c) 以后答案会发生那些变化,会不断跳nxt,直到找到了一个前缀满足其下一个二元组是 (x',c) ,此时我们会增加 min(x,x') 个border,他们的最后一段是一组公差为1的等差数列,对于剩下的串 x-min(x,x') 接着跳nxt

    对于跳的途中,我们若是找到了满足x'=x的二元组 (x',c) ,那将当前二元组的nxt指向找到的这个,否则指向0,因为对于 x'>x 的情况由于任意两个连续二元组的字符不相同,所以必定无法匹配上

    但是这样的优化理论上还是过不了这道题,因为暴力跳 nxt 会被卡成单次 O(n) 的,所以总的复杂度还是 O((n^2)) ,虽然实际上真的过了

    我们考虑建出一个 KMP自动机(借用网上巨佬的说法),我们对于每一个位置维护一下rt[] 数组表示接一个二元组字符为 i 时会跳到哪里,但是由于二元组的长度不同跳到的 nxt 还是不同,所以我们对于每一个 rt[] 都建一颗主席树,主席树的下标是后接二元组对应的长度,然后我们把统计的答案拆开,每一个border拆成跳nxt得到的前缀的长度+延伸的二元组 (x',c) 中匹配到的长度,我们记 len=min(x,x') ,也就是说贡献拆开后的第二部分是一个长度为 len 的等差数列,第一部分我们就放到主席树上面直接区间查询出来

    对于剩下的 x-len 个后缀,我们可能没有办法直接找到一个二元组满足条件的二元组 (x,c) 所以我们需要特判一下,第一个二元组 ((x_1,c_1)) 能不能形成一个符合要求的border,能形成的充要条件就是:(c_1=c)(x_1>x)

    统计完答案之后我们要球盖主席树上的信息,我们把 rt[c] 对应的主席树上 [1,x] 的区间的信息都改成当前的前缀长度,这样会更优,然后继续遍历,将跳到的 nxt 节点的 rt[] 数组的信息, 传给下一个节点

    代码:

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define mk(x,y) make_pair(x,y)
    #define pb push_back
    #define fir first
    #define sec second
    #define inl inline
    #define reg register
    
    using namespace std;
    
    namespace zzc
    {
        inl int read()
        {
            int x=0,f=1;char ch=getchar();
            while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
            while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
            return x*f;
        }
    
        const int maxn = 1e5+5;
        const int maxm = 6.4e6+5;
        const int mod = 998244353;
        const int bj = 10005;
        pii val[maxn];
        vector<int> to[maxn];
        int n,tot,top,tim;
        int id[maxn],ans[maxn],lc[maxm],rc[maxm],sum[maxm],nxt[maxm],tag[maxm],st[maxn],a[maxn],b[maxn],mx[maxn][26],rt[maxn][26];
        
        inl void new_node(int &rt) 
        {
            int tmp=++tot;
            sum[tmp]=sum[rt];
            lc[tmp]=lc[rt];
            rc[tmp]=rc[rt];
            nxt[tmp]=nxt[rt];
            tag[tmp]=tag[rt];
            rt=tmp;
        }
    
        inl void pushup(int rt)
        {
            sum[rt]=(sum[lc[rt]]+sum[rc[rt]])%mod;
        }
        
        inl void form(int rt,int s,int len)
        {
            sum[rt]=1ll*s*len%mod;
            tag[rt]=s;
        }
    
        inl void pushdown(int rt,int l,int r)
        {
            if(!tag[rt]) return ;
            int mid=(l+r)>>1;
            new_node(lc[rt]);form(lc[rt],tag[rt],mid-l+1);
            new_node(rc[rt]);form(rc[rt],tag[rt],r-mid);
            tag[rt]=0;
        }
    
        void modify(int &rt,int l,int r,int pos,int st,int k)
        {
            new_node(rt);
            if(r<pos) return form(rt,st,r-l+1);
            if(l==r)
            {
                form(rt,st,1);
                nxt[rt]=k;
                return ;
            }
            int mid=(l+r)>>1;
            pushdown(rt,l,r);
            modify(lc[rt],l,mid,pos,st,k);
            if(pos>mid) modify(rc[rt],mid+1,r,pos,st,k);
            pushup(rt);
        }
    
        void query(int &rt,int l,int r,int pos,int &res,int &k)
        {
            if(r<pos) return res=(res+sum[rt])%mod,void();
            if(l==r)
            {
                res=(res+sum[rt])%mod;
                k=nxt[rt];
                return ;
            }
            int mid=(l+r)>>1;
            pushdown(rt,l,r);
            query(lc[rt],l,mid,pos,res,k);
            if(pos>mid) query(rc[rt],mid+1,r,pos,res,k);
        }
    
        inl int getsum(int x)
        {
            return (1ll*(x+1)*x/2)%mod;
        }
    
        void dfs(int u)
        {
            top++;
            int x=val[u].sec,y=val[u].fir,_nxt=0;
            a[top]=x;b[top]=b[top-1]+y;
            if(top==1) ans[u]=getsum(y-1);
            else
            {
                ans[u]=(ans[u]+getsum(min(mx[top][x],y)))%mod;
                query(rt[top][x],1,bj,y,ans[u],_nxt);
                if(!_nxt&&a[1]==x&&b[1]<y) _nxt=1,ans[u]=(ans[u]+1ll*b[1]*max(0,y-mx[top][x])%mod)%mod;
            }
            mx[top][x]=max(mx[top][x],y);
            modify(rt[top][x],1,bj,y,b[top-1],top);
            for(auto v:to[u])
            {
                memcpy(mx[top+1],mx[_nxt+1],sizeof(mx[top+1]));
                memcpy(rt[top+1],rt[_nxt+1],sizeof(rt[top+1]));
                ans[v]=ans[u];
                dfs(v);
            }
            top--;
        }
    
        void work()
        {
            int opt,x;
            char ch[5];
            n=read();
            for(reg int i=1;i<=n;i++)
            {
                opt=read();x=read();
                if(opt==1)
                {
                    scanf("%s",ch+1);
                    val[++tim]=mk(x,ch[1]-'a');
                    id[i]=tim;
                    to[id[i-1]].pb(id[i]);
                }
                else id[i]=id[x];
            }
            for(auto v:to[0])
            {
                tot=0;
                memset(rt[1],0,sizeof(rt[1]));
                memset(mx[1],0,sizeof(mx[1]));
                dfs(v);
            }
            for(reg int i=1;i<=n;i++) printf("%d
    ",ans[id[i]]);
        }
    
    
    }
    
    int main()
    {
        zzc::work();
        return 0;
    }
    
  • 相关阅读:
    2018年强化学习领域十篇重要论文(附源码)[转]
    【转载】 再励学习面试真题 (强化学习面试真题)
    枸杞常泡水喝好吗?
    亲历亚马逊、华为机器学习面试,原来考官想听到这些回答[转]
    阿里面试 深度学习[转]
    强化学习(四)用蒙特卡罗法(MC)求解
    初识kaggle,以及记录 kaggle的使用
    强化学习(三)用动态规划(DP)求解
    CORTEX-M3中断的现场保护问题
    地球是圆的怎么还分东西方
  • 原文地址:https://www.cnblogs.com/youth518/p/14408839.html
Copyright © 2020-2023  润新知