BZOJ3946: 无聊的游戏

41 篇文章 0 订阅
15 篇文章 0 订阅

将初始字符串和添加的字符串按照添加顺序 逆序 拼成一个大串S,那么一个串可以被表示成S[l1~r1]+S[l2~r2]…
我们维护height[i]=LCP(s[i-1],s[i]),L~R的LCP=min{ height[L+1~R] }
每次在L~R前面添加一个串T时,L+1~R的height区间加|T|,height[L]和height[R+1]要重新计算,二分加hash计算
考虑怎么计算hash,离线所有操作,将一个对L~R的加操作变成L处加入,R+1处删除,1~n扫一遍所有串,处理出每个串最终在S串的哪些位置取值,然后按时间顺序处理询问,就相当于问线段树上某个坐标往右k个位置的hash值

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 51000;
const int maxp = 610000;
const int maxd = 20;
const int base = 31;

int BASE[maxp];

int n,m,N;
int lr[maxn][2],op[maxn][5];
vector<int>Add[maxn],Del[maxn];
string str[maxn],add[maxn];
char S[maxp],ss[10];

struct segment
{
    int seg[maxp<<2],flag[maxp<<2];
    int lx,rx,c;
    void pushdown(const int x)
    {
        if(!flag[x]) return;
        int fl=flag[x]; flag[x]=0;
        int lc=x<<1,rc=lc|1;
        seg[lc]+=fl,seg[rc]+=fl;
        flag[lc]+=fl,flag[rc]+=fl;
    }
    void upd(const int x,const int l,const int r)
    {
        if(rx<l||r<lx) return;
        if(lx<=l&&r<=rx) { seg[x]+=c,flag[x]+=c;return; }
        pushdown(x);
        int mid=l+r>>1;
        upd(x<<1,l,mid),upd(x<<1|1,mid+1,r);
        seg[x]=min(seg[x<<1],seg[x<<1|1]);
    }
    int query(const int x,const int l,const int r)
    {
        if(rx<l||r<lx) return N+1;
        if(lx<=l&&r<=rx) return seg[x];
        pushdown(x);
        int mid=l+r>>1;
        return min(query(x<<1,l,mid),query(x<<1|1,mid+1,r));
    }
}height,L;
struct Cover
{
    int seg[maxn<<2];
    int lx,rx,loc,c;
    void cov(const int x,const int l,const int r)
    {
        if(rx<l||r<lx) return;
        if(lx<=l&&r<=rx) { seg[x]=c; return; }
        if(seg[x]) seg[x<<1]=seg[x<<1|1]=seg[x],seg[x]=0;
        int mid=l+r>>1;
        cov(x<<1,l,mid); cov(x<<1|1,mid+1,r);
    }
    int query(const int x,const int l,const int r)
    {
        if(l==r) return seg[x];
        if(seg[x]) seg[x<<1]=seg[x<<1|1]=seg[x],seg[x]=0;
        int mid=l+r>>1;
        if(loc<=mid) return query(x<<1,l,mid);
        else return query(x<<1|1,mid+1,r);
    }
}Left;

struct Seg
{
    int lc[maxp*maxd],rc[maxp*maxd],h[maxp*maxd],len[maxp*maxd];
    int lx,rx,cnt;
    void pushup(int x)
    {
        int ls=lc[x],rs=rc[x];
        len[x]=len[ls]+len[rs];
        h[x]=h[ls]+h[rs]*BASE[len[ls]];
    }
    void build(int &x,const int l,const int r)
    {
        if(!x) x=++cnt;
        if(l==r) { len[x]=1,h[x]=S[l]-'a'+1; return; }
        int mid=l+r>>1;
        build(lc[x],l,mid); build(rc[x],mid+1,r);
        pushup(x);
    }
    void merge(int &x,const int &y)
    {
        if(!y) return;
        if(!x) { x=y; return; }
        ++cnt; lc[cnt]=lc[x],rc[cnt]=rc[x]; x=cnt;
        merge(lc[x],lc[y]),merge(rc[x],rc[y]);
        pushup(x);
    }
    void split(int &x,const int y,const int l,const int r)
    {
        if(rx<l||r<lx) return;
        if(lx<=l&&r<=rx) { x=y;return; }
        x=++cnt;
        int mid=l+r>>1;
        split(lc[x],lc[y],l,mid); split(rc[x],rc[y],mid+1,r);
        pushup(x);
    }
    void dec(int &x,const int l,const int r)
    {
        if(rx<l||r<lx) return;
        if(lx<=l&&r<=rx) { x=0; return; }
        ++cnt; lc[cnt]=lc[x],rc[cnt]=rc[x]; x=cnt;
        int mid=l+r>>1;
        dec(lc[x],l,mid); dec(rc[x],mid+1,r);
        if(!lc[x]&&!rc[x]) x=0;
        if(x) pushup(x);
    }
    int ql,ul;
    int query(int x,const int l,const int r)
    {
        if(rx<l||r<lx||ql==ul) return 0;
        if(lx<=l&&r<=rx&&ql+len[x]<=ul) { ql+=len[x]; return h[x]; }
        int mid=l+r>>1;
        int nl=ql,q1=query(lc[x],l,mid); nl=ql-nl;
        int q2=query(rc[x],mid+1,r);
        return q1+q2*BASE[nl];
    }
}seg; int root[maxn];

void BuildSeg()
{
    for(int i=m;i>=1;i--) if(!op[i][0])
    {
        int il=add[i].size();
        op[i][3]=N+1; op[i][4]=N+il;
        for(int j=0;j<il;j++) S[++N]=add[i][j];
    }
    for(int i=1;i<=n;i++)
    {
        int il=str[i].size();
        lr[i][0]=N+1,lr[i][1]=N+il;
        for(int j=0;j<il;j++) S[++N]=str[i][j];

        Left.lx=Left.rx=i; Left.c=lr[i][0]; Left.cov(1,1,n);
        L.lx=L.rx=i; L.c=il; L.upd(1,1,n);
        if(i>1)
        {
            il=min(il,(int)str[i-1].size()); int hi=0;
            while(hi<il&&str[i-1][hi]==str[i][hi]) hi++;
            height.lx=height.rx=i; height.c=hi; height.upd(1,1,n);
        }
    }
    seg.build(root[0],1,N);

    for(int i=1;i<=m;i++) if(!op[i][0])
    {
        Add[op[i][1]].push_back(i);
        if(op[i][2]<n) Del[op[i][2]+1].push_back(i);
    }
    for(int i=1;i<=n;i++)
    { 
        seg.lx=lr[i][0],seg.rx=lr[i][1],seg.split(root[i],root[0],1,N);
        if(i>1) 
        {
            seg.merge(root[i],root[i-1]);
            seg.lx=lr[i-1][0],seg.rx=lr[i-1][1],seg.dec(root[i],1,N);
        }
        for(int j=0;j<Add[i].size();j++)
        {
            int k=Add[i][j]; seg.lx=op[k][3],seg.rx=op[k][4];
            k=0; seg.split(k,root[0],1,N);
            seg.merge(root[i],k);
        }
        for(int j=0;j<Del[i].size();j++)
        {
            int k=Del[i][j]; seg.lx=op[k][3],seg.rx=op[k][4];
            seg.dec(root[i],1,N);
        }
    }
}
bool judge(int i,int mid,int l1,int r1,int l2,int r2)
{
    seg.lx=l1,seg.rx=r1; seg.ql=0,seg.ul=mid; int h1=seg.query(root[i],1,N);
    seg.lx=l2,seg.rx=r2; seg.ql=0,seg.ul=mid; int h2=seg.query(root[i+1],1,N);
    return h1==h2;
}
int cal(int i)
{
    int u=N;
    L.lx=L.rx=i; u=min(u,L.query(1,1,n));
    L.lx=L.rx=i+1; u=min(u,L.query(1,1,n));

    int l1,r1,l2,r2;
    Left.loc=i; l1=Left.query(1,1,n);
    Left.loc=i+1; l2=Left.query(1,1,n);
    r1=lr[i][1],r2=lr[i+1][1];

    int l=1,r=u;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(judge(i,mid,l1,r1,l2,r2)) l=mid+1;
        else r=mid-1;
    }
    return l-1;
}

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    BASE[0]=1; for(int i=1;i<maxp;i++) BASE[i]=BASE[i-1]*base;

    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) cin>>str[i];
    for(int i=1;i<=m;i++)
    {
        scanf("%s",ss);
        op[i][0]=ss[0]=='I'?0:1;
        scanf("%d%d",&op[i][1],&op[i][2]);
        if(!op[i][0]) cin>>add[i];
    }
    BuildSeg();

    for(int i=1;i<=m;i++)
    {
        int l=op[i][1],r=op[i][2];
        if(!op[i][0])
        {
            Left.lx=l,Left.rx=r,Left.c=op[i][3]; Left.cov(1,1,n);
            L.lx=l,L.rx=r,L.c=add[i].size(); L.upd(1,1,n);
            if(l+1<=r) height.lx=l+1,height.rx=r,height.c=add[i].size(),height.upd(1,1,n);
            if(l-1>0) 
            {
                height.lx=height.rx=l,height.c=cal(l-1)-height.query(1,1,n);
                height.upd(1,1,n);
            }
            if(r<n) 
            {
                height.lx=height.rx=r+1,height.c=cal(r)-height.query(1,1,n);
                height.upd(1,1,n);
            }
        }
        else
        {
            if(l==r) L.lx=L.rx=l,printf("%d\n",L.query(1,1,n));
            else height.lx=l+1,height.rx=r,printf("%d\n",height.query(1,1,n));
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值