[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个字符开始的字串,两个字串的公共前缀的长度。比方说,LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0 在研究LCQ函数的过程中,火星人发现了这样的一个关联:如果把该字符串的所有后缀排好序,就可以很快地求出LCQ函数的值;同样,如果求出了LCQ函数的值,也可以很快地将该字符串的后缀排好序。 尽管火星人聪明地找到了求取LCQ函数的快速算法,但不甘心认输的地球人又给火星人出了个难题:在求取LCQ函数的同时,还可以改变字符串本身。具体地说,可以更改字符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。地球人想考验一下,在如此复杂的问题中,火星人是否还能够做到很快地求取LCQ函数的值。

Input

第一行给出初始的字符串。第二行是一个非负整数M,表示操作的个数。接下来的M行,每行描述一个操作。操作有3种,如下所示: 1、 询问。语法:Q x y,x, y均为正整数。功能:计算LCQ(x, y) 限制:1 <= x, y <= 当前字符串长度。 2、 修改。语法:R x d,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。限制:x不超过当前字符串长度。 3、 插入:语法:I x d,x是非负整数,d是字符。功能:在字符串第x个字符之后插入字符d,如果x = 0,则在字符串开头插入。限制:x不超过当前字符串长度。

Output

对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。

Sample Input

7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11

Sample Output

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

对于第1,2个数据,字符串长度自始至终都不超过1,000
对于第3,4,5个数据,没有插入操作。
题意:有LCQ(x,y)操作询问以当前字符串中第x个开始和第y个开始的最长公共前缀的长度。同时还可以插入一个字符或者修改一个字符。
询问最大长度,想到for。。。但同时想到会超时。。。同时字符串比较灰常慢。。。所以可以用hash+二分查找最长公共前缀。我们可以设计一个较好的hash函数,让每个不同的字符串的hash值尽量不重复。因为有插入和修改序列的操作。所以就要动态维护。用到了splay。
然后每个树中的节点记录自己的父亲fa[i],子节点c[i,0]c[i,1],子树(包括本身)构成的字符串的hash值h[i],本身字符a[i],子树(包括自己)中的节点个数sum[i]。
hash的求法:[ord(a[1])*k^0+ord(a[2])*k^1+ord(a[3])*k^2+...+ord(a[n])*k^(n-1)] mod maxn
那在旋转的时候怎么修改h[i]呢?h[x]:=h[c[x,0]]+a[x]*k^(sum[c[x,0]])+h[c[x,1]]*k^(sum[c[x,0]]+1)
每次二分长度只要把要匹配的子串转成根的右节点的左子树所对应的串。比较两个两棵子树的hash值是否相同判断子串是否相同。
可以加几个特判:
1.判断首字母位置是否相同。相同输出最大长度即可。
2.判断首字母是否是不同字母。不同则输出0。
3.最大长度特判。有的数据整列字符都是一样的。。。。
 
AC CODE
program hy_1014;
const maxn= 9875321 ;
var h: array [ 0..100010 ] of int64 ;
     d: array [ 1..100010 ] of int64 ;
     a,sum: array [ 0..100010 ] of int64 ;
     fa: array [ 0..100010 ] of longint ;
     ll,rr: array [ 1..150010 ] of longint ;
     c: array [ 0..100010 , 0..1 ] of longint ;
     size,k,kk,root,n,m,all: longint ;
     hh,ch: array [ 1..150010 ] of char ;
//============================================================================
procedure update(x: longint );
var p,q: longint ;
begin
   sum[x]:=sum[c[x, 0 ]]+sum[c[x, 1 ]]+ 1 ;
   p:=c[x, 0 ]; q:=c[x, 1 ];
   h[x]:=(h[p]+a[x]*d[sum[p]+ 1 ]+h[q]*d[sum[p]+ 2 ]) mod maxn;   //更新hash值。
end ;
//============================================================================
procedure build(l,r: longint );   //一开始用递归建树,防止树的深度太大。
var mid,tmp: longint ;
begin
   mid:=(l+r) shr 1 ;
   if mid>l then
   begin
     build(l,mid- 1 );
     tmp:=(l+mid- 1 ) shr 1 ;
     c[mid, 0 ]:=tmp; fa[tmp]:=mid;
   end ;
   if mid<r then
   begin
     build(mid+ 1 ,r);
     tmp:=(mid+ 1 +r) shr 1 ;
     c[mid, 1 ]:=tmp; fa[tmp]:=mid;
   end ; update(mid);
end ;
//============================================================================
procedure init;
var i,len: longint ;
     st: ansistring ;
     chh: char ;
begin
   readln(st); len:=length(st);
   for i:= 2 to len+ 1 do  //在头尾预留两个单元,splay时就总能找到l-1和r+1。
   begin
     a[i]:=ord(st[i- 1 ])-ord( 'a' );
     h[i]:=a[i];
   end ; d[ 1 ]:= 1 ;
   for i:= 2 to 100010 do
     d[i]:=(d[i- 1 ]* 27 ) mod maxn;   //预处理k的n次方(见hash函数)。
   build( 1 ,len+ 2 ); root:=(len+ 3 ) shr 1 ;
   readln(m); size:=len+ 2 ;
   for i:= 1 to m do
   begin
     read(ch[i]);
     if ch[i]= 'Q' then
     begin
       inc(all); readln(ll[i],rr[i]);
     end else readln(ll[i],chh,hh[i]);
   end ;
end ;
//============================================================================
function find(x: longint ): longint ;
begin
   if sum[c[x, 0 ]]+ 1 =k then exit(x);
   if sum[c[x, 0 ]]<k- 1 then
   begin
     dec(k,sum[c[x, 0 ]]+ 1 );
     find:=find(c[x, 1 ]);
   end else find:=find(c[x, 0 ]);
end ;
//============================================================================
procedure rotate( var root: longint ; x: longint );
var y,z,p,q: longint ;
begin
   y:=fa[x]; z:=fa[y];
   if c[y, 0 ]=x then p:= 0 else p:= 1 ;
   q:=p xor 1 ;
   if y=root then root:=x else
     if c[z, 0 ]=y then c[z, 0 ]:=x else c[z, 1 ]:=x;
   fa[x]:=z; fa[y]:=x; fa[c[x,q]]:=y;
   c[y,p]:=c[x,q]; c[x,q]:=y;
   update(y); update(x);
end ;
//============================================================================
procedure splay( var root: longint ; x: longint );
var y,z: longint ;
begin
   while x<>root do
   begin
     y:=fa[x]; z:=fa[y];
     if y<>root then
       if (c[y, 0 ]=x) xor (c[z, 0 ]=y) then
       rotate(root,x) else rotate(root,y);
     rotate(root,x);
   end ;
end ;
//============================================================================
procedure insert(x: longint );
begin
   if x= 0 then
   begin
     inc(size); h[size]:=kk; sum[size]:= 1 ;
     a[size]:=h[size]; exit;
   end ;
   if sum[c[x, 0 ]]>=k then
   begin
     insert(c[x, 0 ]);
     if c[x, 0 ]= 0 then begin c[x, 0 ]:=size; fa[size]:=x; end ;
   end else
   begin
     k:=k-sum[c[x, 0 ]]- 1 ; insert(c[x, 1 ]);
     if c[x, 1 ]= 0 then begin c[x, 1 ]:=size; fa[size]:=x; end ;
   end ; update(x);
end ;
//============================================================================
procedure ask(x: longint );
var xx,yy,l,r,ans,l1,l2,left1,left2: longint ;
     hash1,hash2,s,t,mid,tt: longint ;
begin
   if rr[x]<ll[x] then
   begin
     tt:=rr[x]; rr[x]:=ll[x]; ll[x]:=tt;
   end ;
   if ll[x]=rr[x] then
   begin writeln (size- 1 -ll[x]); exit; end ;
   k:=ll[x]; l:=find(root); splay(root,l);
   k:=rr[x]; r:=find(root);
   k:=ll[x]+ 1 ; xx:=find(root); k:=rr[x]+ 1 ; yy:=find(root);
   if a[xx]<>a[yy] then
   begin writeln ( '0' ); exit; end ;
   s:= 1 ; t:=size-rr[x]- 1 ; ans:= 0 ;
     k:=ll[x]+t+ 1 ; l1:=find(root);
     k:=rr[x]+t+ 1 ; l2:=find(root);
     splay(c[root, 1 ],l1); hash1:=h[c[l1, 0 ]];
     splay(c[root, 1 ],r); splay(c[r, 1 ],l2); hash2:=h[c[l2, 0 ]];
     if hash1=hash2 then begin writeln (t); exit; end ; //这里特判了最长长度的串。。有的数据太猥琐。。。
   dec(t); 
   repeat
     mid:=(s+t) shr 1 ;
     k:=ll[x]+mid+ 1 ; l1:=find(root);
     k:=rr[x]+mid+ 1 ; l2:=find(root);
     splay(c[root, 1 ],l1); hash1:=h[c[l1, 0 ]];
     splay(c[root, 1 ],r); splay(c[r, 1 ],l2); hash2:=h[c[l2, 0 ]];
     if hash1=hash2 then
     begin s:=mid+ 1 ; ans:=mid; end else t:=mid- 1 ;
   until s>t; writeln (ans);
end ;
//============================================================================
procedure rep(x: longint );
var z,p,q: longint ;
begin
   k:=ll[x]+ 1 ; z:=find(root); splay(root,z);
   a[z]:=ord(hh[x])-ord( 'a' ); p:=c[root, 0 ]; q:=c[root, 1 ];
   h[z]:=(h[p]+a[z]*d[sum[p]+ 1 ]+h[q]*d[sum[p]+ 2 ]);
end ;
//============================================================================
procedure ins(x: longint );
begin
   k:=ll[x]+ 1 ; kk:=ord(hh[x])-ord( 'a' ); insert(root);
   splay(root,size);
end ;
//============================================================================
procedure work;
var i: longint ;
begin
   for i:= 1 to m do
   begin
     if ch[i]= 'Q' then
     begin
       ask(i); dec(all);
       if all= 0 then halt;
     end else if ch[i]= 'R' then rep(i) else ins(i);
   end ;
end ;
//============================================================================
begin
   init;
   work;
end .
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用[1],dp[u][j]表示在u子树中选取恰好j个人时能获得的最大价值。而根据引用,该问题的时间复杂度为O(log2​104×nm)。 对于洛谷P2143 [JSOI2010] 巨额奖金问题,我们可以使用动态规划来解决。具体步骤如下: 1. 首先,我们需要构建一棵树来表示员工之间的关系。树的根节点表示公司的总经理,其他节点表示员工。每个节点都有一个权值,表示该员工的奖金金额。 2. 接下来,我们可以使用动态规划来计算每个节点的dp值。对于每个节点u,我们可以考虑两种情况: - 如果选择节点u,则dp[u][j] = dp[v][j-1] + value[u],其中v是u的子节点,value[u]表示节点u的奖金金额。 - 如果不选择节点u,则dp[u][j] = max(dp[v][j]),其中v是u的子节点。 3. 最后,我们可以通过遍历树的所有节点,计算出dp[u][j]的最大值,即为所求的巨额奖金。 下面是一个示例代码,演示了如何使用动态规划来解决洛谷P2143 [JSOI2010] 巨额奖金问题: ```python # 构建树的数据结构 class Node: def __init__(self, value): self.value = value self.children = [] # 动态规划求解最大奖金 def max_bonus(root, j): dp = [[0] * (j+1) for _ in range(len(root)+1)] def dfs(node): if not node: return for child in node.children: dfs(child) for k in range(j, 0, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-1] + node.value) for child in node.children: for k in range(j, 0, -1): for l in range(k-1, -1, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-l-1] + dp[child.value][l]) dfs(root) return dp[root.value][j] # 构建树 root = Node(1) root.children.append(Node(2)) root.children.append(Node(3)) root.children[0].children.append(Node(4)) root.children[0].children.append(Node(5)) root.children[1].children.append(Node(6)) # 求解最大奖金 j = 3 max_bonus_value = max_bonus(root, j) print("最大奖金为:", max_bonus_value) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值