[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] 火星人
原文地址:https://www.cnblogs.com/eternhope/p/9609103.html