【JZOJ5497】塔

Description

有一个塔,他的名字叫做粽粑,粽粑的每一层都有一个颜色 .
粽粑非常厉害,它在吸收天地精华之后会长高.粽粑的长高方式有两种:
1.在塔顶长出一层.
2.在塔底长出一层,即原来的第一层变成第二层,第二层变成第三层,以此类推,新长出来的是第一层.
粽粑有可能在某个时刻不是很开心,这个时候它会撤销它的前若干次长高.
你现在想知道粽粑长高的奥秘,于是找到了粽粑,发现它的入口上写着这么一句话:要进入粽粑,请找出一段最长的塔的区间,满足翻转后颜色不变.
粽粑会不断的长高(或撤销),所以它每次长高(或撤销)后你都要回答.为了你的方便,粽粑一开始的高度为0。

Solution

这题就是一道SB字符串题,只要前面后面各维护一个回文树,每加入一个就更新一下,撤销就暴力撤销,时间复杂度是 O(nlog2σ) ,好像很优秀,让我们看看 n107 ……还是放弃吧。

我们发现每次加入只会使答案不变,或+1或+2,那么只要维护前后的哈希值就够了。

Code

写的巨丑,常数极大,还T了一个点。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
#define ll long long
using namespace std;
const int N=10000010,mo=1000000007,ny=224299067;
char s[N*3];
struct node{
    int l,r,ln;
    int fr,rf;
    int en,re;
}st[N];
short int a[N*3];
int z[N],top=0;
void add_fr(int &v,int ln,int t) {v=((ll)t*z[ln]+v)%mo;}
void add_en(int &v,int t) {v=((ll)v*z[1]+t)%mo;}
void del_fr(int &v,int ln,int t) {v=(((ll)v-(ll)t*z[ln-1])%mo+mo)%mo;}
void del_en(int &v,int t) {v=(((ll)v-t+mo)%mo*ny)%mo;}
int main()
{
    freopen("tower.in","r",stdin);
    freopen("tower.out","w",stdout);
    int q;
    scanf("%d",&q);
    scanf("%s",s);
    z[0]=1;
    fo(i,1,q) z[i]=(ll)z[i-1]*107%mo;
    st[0].l=q+1,st[0].r=q;
    int lans=0;
    ll ans=0;
    fo(i,0,q-1){
        int op=s[i*3]-'0',t=((s[i*3+1]-'0')*10+s[i*3+2]-'0'+lans)%100;
        if(op==1){
            ++t;
            st[top+1]=st[top],++top;
            int fr=st[top].fr,rf=st[top].rf,ln=st[top].ln;
            int l=st[top].l,r=st[top].r;
            add_fr(fr,ln,t),add_en(rf,t);
            st[top].l--,a[l-1]=t,a[l-2]=a[r+1]=0;
            st[top].fr=fr,st[top].rf=rf;
            del_en(st[top].fr,a[l+ln-1]),del_fr(st[top].rf,ln+1,a[l+ln-1]);
            if(fr==rf) st[top].fr=fr,st[top].rf=rf,st[top].ln=ln+1;
            add_en(fr,a[l+ln]),add_fr(rf,ln+1,a[l+ln]);
            if(fr==rf) st[top].fr=fr,st[top].rf=rf,st[top].ln=ln+2;
            fo(j,1,st[top].ln-ln) add_en(st[top].en,a[r-ln+1-j]),add_fr(st[top].re,ln+j-1,a[r-ln+1-j]);
        }
        else if(op==2){
            ++t;
            st[top+1]=st[top],++top;
            int en=st[top].en,re=st[top].re,ln=st[top].ln;
            int l=st[top].l,r=st[top].r;
            add_fr(en,ln,t),add_en(re,t);
            st[top].r++,a[r+1]=t,a[l-1]=a[r+2]=0;
            st[top].en=en,st[top].re=re;
            del_en(st[top].en,a[r-ln+1]),del_fr(st[top].re,ln+1,a[r-ln+1]);
            if(en==re) st[top].en=en,st[top].re=re,st[top].ln=ln+1;
            add_en(en,a[r-ln]),add_fr(re,ln+1,a[r-ln]);
            if(en==re) st[top].en=en,st[top].re=re,st[top].ln=ln+2;
            fo(j,1,st[top].ln-ln) add_en(st[top].fr,a[l+ln-1+j]),add_fr(st[top].rf,ln+j-1,a[l+ln-1+j]);
        }
        else top-=t;
        lans=st[top].ln,ans+=lans;
    }
    printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值