[JSOI2008] 火星人

[bzoj1014] [JSOI2008] 火星人prefix 题目传送门

[洛谷P4036] [JSOI2008] 火星人 题目传送门

先考虑不带插入和修改的情况。

我们可以先把字符串hash了,用hash值二分答案(我用倍增答案)求出最长公共前缀。

但是如果有插入修改呢?

用splay维护hash值,每个节点的值v就是该位字母的值(char-‘a’+1)。

splay的每个子树都代表字符串的一个部分。

在每个点上记录一个sum,代表这个点的子树代表的字符串的hash值。

显然这个值可以用左右子树的sum、该节点的v 以及hash时用的seed的(左子树大小)次幂来表示。

这样查询、插入和修改都是很简单了。

在树上转一下,更新或查询即可。

询问时还是二分答案(倍增答案),利用splay查询hash值进行比较。

这里还是define了一个empty代表空位置。

注意check函数的边界判断QwQ

if( (x+nw>len) || (y+nw>len) ) return false;

在这里卡了一上午呜呜呜......

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #define empty 100002
  5 #define id(x) (s[f[x]][1]==x)
  6 #define ull unsigned long long
  7 using namespace std;
  8 
  9 int m,len;
 10 char str[100010];
 11 ull hp[100010],v[100010],sum[100010],sd=37;
 12 int s[100010][2],f[100010],root,sz[100010];
 13 
 14 void pushup(int p)
 15 {
 16     sz[p]=sz[s[p][0]]+sz[s[p][1]]+1;
 17     sum[p]=sum[s[p][0]]*hp[sz[s[p][1]]+1]+sum[s[p][1]]+v[p]*hp[sz[s[p][1]]];
 18 }
 19 
 20 int build(int l,int r)
 21 {
 22     if(l>r)return empty;
 23     int mid=(l+r)>>1;
 24     s[mid][0]=build(l,mid-1);
 25     s[mid][1]=build(mid+1,r);
 26     if(s[mid][0]!=empty)f[s[mid][0]]=mid;
 27     if(s[mid][1]!=empty)f[s[mid][1]]=mid;
 28     v[mid]=str[mid]-'a'+1;
 29     pushup(mid);
 30     return mid;
 31 }
 32 
 33 void rotate(int p)
 34 {
 35     int fa=f[p];
 36     int k=id(p);
 37     s[fa][k]=s[p][!k];
 38     s[p][!k]=fa;
 39     s[f[fa]][id(fa)]=p;
 40     f[p]=f[fa];
 41     f[s[fa][k]]=fa;
 42     f[fa]=p;
 43     pushup(fa);
 44     pushup(p);
 45 }
 46 
 47 void splay(int p,int g)
 48 {
 49     while(f[p]!=g)
 50     {
 51         int fa=f[p];
 52         if(f[fa]==g)
 53         {
 54             rotate(p);
 55             break;
 56         }
 57         if(id(p)^id(fa))rotate(p);
 58         else rotate(fa);
 59         rotate(p);
 60     }
 61     if(g==empty)root=p;
 62 }
 63 
 64 int qpos(int p,int rk)
 65 {
 66     if(sz[s[p][0]]>=rk)return qpos(s[p][0],rk);
 67     else if(sz[s[p][0]]+1==rk)return p;
 68     else return qpos(s[p][1],rk-1-sz[s[p][0]]);
 69 }
 70 
 71 bool check(int x,int y,int nw)
 72 {
 73     if((x+nw>len)||(y+nw>len))
 74         return false;
 75     splay(qpos(root,x),empty);
 76     splay(qpos(root,x+nw+1),root);
 77     ull hx=sum[s[s[root][1]][0]];
 78     splay(qpos(root,y),empty);
 79     splay(qpos(root,y+nw+1),root);
 80     ull hy=sum[s[s[root][1]][0]];
 81     return (hx==hy);
 82 }
 83 
 84 void query()
 85 {
 86     int x,y;
 87     scanf("%d%d",&x,&y);
 88     int ans=0;
 89     for(int i=20;i>=0;i--)
 90         if(check(x,y,ans|(1<<i)))ans|=(1<<i);
 91     printf("%d\n",ans);
 92 }
 93 
 94 void repair()
 95 {
 96     char c[5];
 97     int x;
 98     scanf("%d",&x);
 99     scanf("%s",c+1);
100     int tar=qpos(root,x+1);
101     splay(tar,empty);
102     v[root]=c[1]-'a'+1;
103     pushup(root);
104 }
105 
106 void in()
107 {
108     char c[5];
109     int x;
110     scanf("%d",&x);
111     scanf("%s",c+1);
112     splay(qpos(root,x+1),empty);
113     splay(qpos(root,x+2),root);
114     f[++len]=s[root][1];
115     s[s[root][1]][0]=len;
116     v[len]=sum[len]=c[1]-'a'+1;
117     sz[len]=1;
118     pushup(s[root][1]);
119     pushup(root);
120 }
121 
122 int main()
123 {
124     scanf("%s",str+1);
125     len=strlen(str+1);
126     hp[0]=1;
127     for(int i=1;i<=100005;i++)hp[i]=hp[i-1]*sd;
128     for(int i=0;i<=100001;i++)s[i][0]=s[i][1]=f[i]=empty;
129     str[0]=str[++len]='a'-1;
130     root=build(0,len);
131     scanf("%d",&m);
132     while(m--)
133     {
134         char op[5];
135         scanf("%s",op+1);
136         if(op[1]=='Q')query();
137         if(op[1]=='R')repair();
138         if(op[1]=='I')in();
139     }
140     return 0;
141 }
[JSOI2008] 火星人

之前多次提到倍增答案,那么什么是倍增答案呢?

想知道的点这里哦~

posted @ 2018-09-08 14:15  cervusky  阅读(179)  评论(0编辑  收藏  举报

Contact with me