bzoj1014 [JSOI2008]火星人prefix ( splay + hash )

bzoj1014 [JSOI2008]火星人prefix

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=1014

题意:
给定一个字符串,比方说,有这样一个字符串:madamimadam,
我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m
有3种操作:
1、询问。语法:Q x y,x, y均为正整数。
  功能:计算LCQ(x, y)
2、修改。语法:R x d,x是正整数,d是字符。
  功能:将字符串中第x个数修改为字符d。
  限制:x不超过当前字符串长度。
3、插入:语法:I x d,x是非负整数,d是字符。
  功能:在字符串第x个字符之后插入字符d,如果x = 0,则在字符串开头插入。
  限制:x不超过当前字符串长度。

第一行给出初始的字符串。第二行是一个非负整数M,表示操作的个数。

数据范围
1、所有字符串自始至终都只有小写字母构成。
2、M<=150,000
3、字符串长度L自始至终都满足L<=100,000
4、询问操作的个数不超过10,000个。

题解:
复习一发spaly。

首先我们知道二分比较hash值可以log地得到两个串的最长公共前缀,而给出的替换和插入操作可以让我们想到splay维护。

于是我们用splay维护字符串,每个节点上存有它控制的区间的hash值。每次查询两个后缀的最长公共前缀,只需要二分长度,然后把这个区间转上去,重算hash值,比较这两个区间的hash值是否相同即可。

( wa点:size没搞对,二分的时候越界了。改回来时影响了关于查询的两个串是同一个串的情况,其实不用特判。)

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LL unsigned long long
using namespace std;
const int N=100010;
const int M=150010;
const LL base=131;
int root,tail=0,fa[N];
struct node
{
    int ch[2],size;
    LL val,key;
}tr[N];
int m,len;
LL mi[N];
char s[M],opt[5];
void update(int x)
{
    int ls=tr[x].ch[0]; int rs=tr[x].ch[1];
    tr[x].key=tr[x].val; tr[x].size=1;
    if(rs)
    {
        tr[x].size+=tr[rs].size;
        tr[x].key=tr[x].key*mi[tr[rs].size]+tr[rs].key;
    }
    if(ls)
    {
        tr[x].size+=tr[ls].size;
        tr[x].key=tr[ls].key*mi[tr[x].size-tr[ls].size]+tr[x].key;
    }
    return;
}
void build(int &nd,int lf,int rg,int f)
{
    if(lf>rg) return;
    nd=++tail; fa[nd]=f;
    tr[nd].ch[0]=tr[nd].ch[1]=0;
    if(lf==rg)
    {
        tr[nd].size=1;
        tr[nd].val=tr[nd].key=(int)s[lf];
        return;
    }
    int mid=(lf+rg)>>1;
    tr[nd].val=s[mid];
    build(tr[nd].ch[0],lf,mid-1,nd);
    build(tr[nd].ch[1],mid+1,rg,nd);
    update(nd);

}
int find(int nd,int pos)
{   
    int ls=tr[nd].ch[0]; int rs=tr[nd].ch[1];
    if(pos==tr[ls].size+1) return nd;
    else if(pos<=tr[ls].size) return find(tr[nd].ch[0],pos);
    else return find(tr[nd].ch[1],pos-tr[ls].size-1);
}
void rotate(int x,int &k)
{
    int y=fa[x]; int z=fa[y];
    int l,r;
    if(x==tr[y].ch[0]) l=0; else l=1; r=l^1;
    if(y==k) k=x;
    else if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x;
    fa[x]=z;
    tr[y].ch[l]=tr[x].ch[r];
    fa[tr[x].ch[r]]=y;
    tr[x].ch[r]=y; fa[y]=x;
    update(y); update(x);
}
void splay(int x,int &k)
{
    while(x!=k)
    {
        int y=fa[x]; int z=fa[y];
        if(y!=k)
        {
            if((tr[y].ch[0]==x)^(tr[z].ch[0]==y))
            rotate(x,k);
            else rotate(y,k);
        }   
        rotate(x,k);        
    }
}
LL check(int l,int r)
{
    splay(l,root); splay(r,tr[l].ch[1]);
    int nd=tr[tr[l].ch[1]].ch[0];
    return tr[nd].key;
}
int query(int p,int q)      //询问x,y的lcp 
{

    int x=find(root,p); int y=find(root,q);
    if(tr[x].val!=tr[y].val) return 0;
    int lx,rx,ly,ry;
    lx=find(root,p-1);  ly=find(root,q-1);
    int sz=tr[root].size;
    int lf=1; int rg=min(sz-p,sz-q);
    while(lf+1<rg)
    {
        int mid=(lf+rg)>>1;
        rx=find(root,p+mid); 
        LL v1=check(lx,rx); 
        ry=find(root,q+mid);
        LL v2=check(ly,ry);
        if(v1==v2) lf=mid;
        else rg=mid;
    }
    int ans;
    rx=find(root,p+rg); 
    LL v1=check(lx,rx); 
    ry=find(root,q+rg);
    LL v2=check(ly,ry);
    if(v1==v2) ans=rg;
    else ans=lf;
    return ans;
}
int main()
{
    mi[0]=1;
    for(int i=1;i<N;i++)
    mi[i]=mi[i-1]*base;

    scanf("%s",s+2);
    len=strlen(s+2);
    tr[0].size=0;
    build(root,1,len+2,0);

    scanf("%d",&m);
    while(m--)
    {   
        scanf("%s",opt);
        if(opt[0]=='Q')
        {
            int x,y;
            scanf("%d%d",&x,&y);
            x++; y++;
            printf("%d\n",query(x,y));
        }
        else if(opt[0]=='R')
        {
            int pos; char ccc[5];
            scanf("%d",&pos); scanf("%s",ccc);
            pos++;
            int c=(int)ccc[0];
            int nd=find(root,pos);
            splay(nd,root);
            tr[nd].val=c;
            update(nd);
        }
        else
        {
            int pos; char ccc[5];
            scanf("%d",&pos); scanf("%s",ccc);
            pos++;
            int c=(int)ccc[0];
            int f=find(root,pos);
            splay(f,root);
            int nd=++tail;
            tr[nd].val=c; 
            tr[nd].ch[1]=tr[f].ch[1]; fa[tr[nd].ch[1]]=nd;
            tr[f].ch[1]=nd;fa[nd]=f;
            tr[nd].ch[0]=0;
            update(nd); update(f);
        }
    }
    return 0;
}

专心,专心。

不理我算啦 (′へ` )

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值