【BZOJ】1014 [JSOI2008]火星人prefix

【算法】splay

【题解】对于每个结点维护其子树串的hash值,前面为高位,后面为低位。

sum[x]=sum[L]*base[s[R]+1]+A[x]*base[s[R]]+sum[R],其中sum为哈希,base为乘权,A为数值(即字符)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010,inf=0x3f3f3f3f,bases=233;
int f[maxn],t[maxn][2],s[maxn],A[maxn],a[maxn],sz=0,root,n;
unsigned long long sum[maxn],a_,b_,b[maxn];
int Node(int fa,int num)
{
    sz++;
    f[sz]=fa;t[sz][0]=t[sz][1]=0;
    s[sz]=1;A[sz]=sum[sz]=num;
    return sz;
}
void count(int x)
{
    s[x]=s[t[x][0]]+1+s[t[x][1]];
    sum[x]=sum[t[x][0]]*b[s[t[x][1]]+1]+1ull*A[x]*b[s[t[x][1]]]+sum[t[x][1]];
}
void build(int fa,int &x,int l,int r)
{
    if(l>r)return;
    int mid=(l+r)>>1;
    x=Node(fa,a[mid]);
    build(x,t[x][0],l,mid-1);
    build(x,t[x][1],mid+1,r);
    count(x);
}
void rotate(int x)
{
    int k=x==t[f[x]][1];
    int y=f[x];
    t[y][k]=t[x][!k];f[t[x][!k]]=y;
    if(f[y])t[f[y]][y==t[f[y]][1]]=x;f[x]=f[y];f[y]=x;
    t[x][!k]=y;
    sum[x]=sum[y];s[x]=s[y];
    count(y);
}
void splay(int x,int r)
{
    for(int fa=f[r];f[x]!=fa;)
    {
        if(f[f[x]]==fa){rotate(x);return;}
        int X=x==t[x][1],Y=f[x]==t[f[f[x]]][1];
        if(X^Y)rotate(x),rotate(x);
         else rotate(f[x]),rotate(x);
    }
}
void find(int &x,int k)
{
    for(int i=x;i;)
    {
        if(k<=s[t[i][0]]){i=t[i][0];continue;}
        if(k==s[t[i][0]]+1){splay(i,x);x=i;return;}
        k-=s[t[i][0]]+1;i=t[i][1];
    }
}
bool work(int x,int y,int longs)
{
    if(x+longs-1>n||y+longs-1>n)return 0;
    find(root,x);find(t[root][1],longs+1);
    a_=sum[t[t[root][1]][0]];
    find(root,y);find(t[root][1],longs+1);
    b_=sum[t[t[root][1]][0]];
    if(a_==b_)return 1;
    return 0;
}
void ask()
{
    int x,y;
    scanf("%d%d",&x,&y);
    int l=0,r=maxn;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(work(x,y,mid))l=mid+1;
         else r=mid;
    }
    printf("%d\n",l-1);
}
void repair()
{
    int x;char c;
    scanf("%d %c",&x,&c);
    find(root,x+1);
    A[root]=c-‘a‘+1;
    count(root);
}
void insert()
{
    n++;
    int x;char c;
    scanf("%d %c",&x,&c);
    find(root,x+1);find(t[root][1],1);
    int y=Node(t[root][1],c-‘a‘+1);
    t[t[root][1]][0]=y;
    f[y]=t[root][1];
    count(t[root][1]);
    count(root);
}
char str[maxn];
int main()
{
    scanf("%s",str+1);
    n=strlen(str+1);
    for(int i=1;i<=n;i++)a[i]=str[i]-‘a‘+1;
    b[0]=1;
    for(int i=1;i<=maxn;i++)b[i]=b[i-1]*bases;
    root=a[0]=a[n+1]=0;
    build(0,root,0,n+1);
    int m;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",str);//不要用%c会读空格,用%s读到空格会停。
        if(str[0]==‘Q‘)ask();
        if(str[0]==‘R‘)repair();
        if(str[0]==‘I‘)insert();
    }
    return 0;
}

时间: 2024-10-15 05:22:32

【BZOJ】1014 [JSOI2008]火星人prefix的相关文章

bzoj 1014: [JSOI2008]火星人prefix hash &amp;&amp; splay

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3154  Solved: 948[Submit][Status] Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人

BZOJ 1014: [JSOI2008]火星人prefix( splay + hash )

用splay维护序列, 二分+hash来判断LCQ.. #include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; const int maxn = 100009; const int P = 1000173169; ull K[maxn]; int N; char S[maxn]; struct Node { Node *ch[2], *p; int s, v; ull h; inline void

求帮看!!!!BZOJ 1014 [JSOI2008]火星人prefix

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4164  Solved: 1277[Submit][Status][Discuss] Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d

[BZOJ 1014] [JSOI2008] 火星人prefix 【Splay + Hash】

题目链接:BZOJ - 1014 题目分析 求两个串的 LCP ,一种常见的方法就是 二分+Hash,对于一个二分的长度 l,如果两个串的长度为 l 的前缀的Hash相等,就认为他们相等. 这里有修改字符和插入字符的操作,所以用 Splay 来维护串的 Hash 值. 一个节点的值就是它的子树表示的字串的 Hash 值. 使用 unsigned long long 然后自然溢出就不需要 mod 了,速度会快很多. 代码 #include <iostream> #include <cstd

BZOJ 1014: [JSOI2008]火星人prefix

Time Limit: 10 Sec  Memory Limit: 162 MB Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam, 我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在, 火星人定义了一个函数LCQ(x, y),表示:该字符串中第x个字符开始的字串,与该字符串中第y个字符开始的字串 ,两个字串的公共前缀的长

[BZOJ 1014][JSOI2008]火星人prefix(Splay+二分+hash)

Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam, 我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在, 火星人定义了一个函数LCQ(x, y),表示:该字符串中第x个字符开始的字串,与该字符串中第y个字符开始的字串 ,两个字串的公共前缀的长度.比方说,LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(

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

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1014 [题意] 给定一个字符串,要求提供修改一个字符,插入一个字符,查询两个后缀LCP的功能. [思路]  splay维护字符串的哈希值.因为要提供区间,splay采用先查找后调整至根的写法. 一个结点的hash值为: ch[0]->h * X^(ch[1]->s+1)+v * X^(ch[1]->s)+ch[1]->h     对于一个询问每次二分长度,提取区间后比较

BZOJ 1014 JSOI2008 火星人prefix Splay+Hash+二分

题目大意:给定一个字符串,提供下列操作: 1.查询从x开始的后缀和从y开始的后缀的最长公共前缀长度 2.将x位置的字符修改为y 3.在x位置的字符后面插入字符y 看到这题一开始我先懵住了...这啥..我第一时间想到的是后缀数据结构 但是不会写 而且后缀数据结构也不支持修改操作 后来无奈找了题解才知道是Hash+二分... 太强大了 Hash+二分打爆一切啊 用Splay维护这个字符串的修改和插入操作 每个节点维护子串的Hash值 判断时二分找到最长公共前缀 不过这道题还有一些注意事项 1.此题不

bzoj 1014[JSOI2008]火星人prefix - 二分 + hash + splay

我们发现要支持修改操作,所以后缀数组就不适用了 查询两个字符串的lcp有两个很常见的算法, 后缀数组和 二分哈希 所以对于字符串的修改我们用一个splay 来维护, 平衡树每个节点表示的是对应子树的字符串的哈希值. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define LL long long 6 using namesp

bzoj 1014 [JSOI2008]火星人prefix (splay+二分答案+字符串hash)

题目大意:维护一个字符串,支持插入字符和替换字符的操作,以及查询该字符串两个后缀的最长公共前缀长度 乍一看以为是后缀数组,然而并没有可持久化后缀数组(雾) 看题解才知道这是一道splay题,首先要对splay维护区间信息有一定了解 splay维护,插入字符,替换字符 而它的字树内所有儿子的中序遍历的hash值也可以通过splay维护  (这个推导式似乎烂大街了) 而后缀就是把i-1拎到根节点,然后把n+1拎到根节点的右儿子上,它的左儿子表示的就是hash值 至于如何查公共前缀呢?二分答案啊!询问