[JSOI2008]火星人

标签:Splay+Hash

题解:

  首先肯定不是后缀数组,当然splay比后缀数组要简单一些。
  求解这个问题,我们可以二分,对于两个串A,B他们的最长公共前缀是可以二分出来的。
  那么我们对于每一个后缀,二分一下,但是需要维护这一个东西,那么我们使用splay来维护序列。按照下标为关键字来把初始字符串构造成一棵splay,然后维护一个Hash值代表子树的字符串的Hash值。每次将L-1旋转到根,再把R+1旋转到根的右儿子,这样就可以查询一段区间的Hash值了。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 #define LL long long
  6 using namespace std;
  7 const int MAXN=160000,mod=192608173;
  8 char s[MAXN];
  9 int n,m,root,CNT;
 10 int v[MAXN];
 11 int sz[MAXN],fa[MAXN],ha[MAXN],val[MAXN],ch[MAXN][2];
 12 void Update(int x)
 13 {
 14   int ls=ch[x][0],rs=ch[x][1];
 15   sz[x]=sz[ls]+sz[rs]+1;
 16   ha[x]=ha[ls]+(LL)val[x]*v[sz[ls]]%mod+(LL)ha[rs]*v[sz[ls]+1]%mod;
 17   ha[x]%=mod;
 18 }
 19 void Rotate(int x,int &y)
 20 {
 21   int old=fa[x],oldf=fa[old],op=ch[old][1]==x;
 22   if(old==y) y=x;
 23   else ch[oldf][ch[oldf][1]==old]=x;
 24   fa[x]=oldf;
 25   fa[ch[x][op^1]]=old; ch[old][op]=ch[x][op^1];
 26   fa[old]=x; ch[x][op^1]=old;
 27   Update(old); Update(x);
 28 }
 29 void Splay(int x,int &y)
 30 {
 31   int old,oldf;
 32   while(x!=y)
 33     {
 34       old=fa[x]; oldf=fa[old];
 35       if(old!=y){
 36         if((ch[old][0]==x)^(ch[oldf][0]==old)) Rotate(x,y);
 37         else Rotate(old,y);
 38       }
 39       Rotate(x,y);
 40     }
 41 }
 42 void Build(int &x,int ll,int rr,int FA)
 43 {
 44   x=++CNT; int mid=(ll+rr)/2;
 45   fa[x]=FA; val[x]=s[mid]-'a'+1;
 46   if(ll<mid)Build(ch[x][0],ll,mid-1,x);
 47   if(rr>mid)Build(ch[x][1],mid+1,rr,x);
 48   Update(x);
 49 }
 50 int find(int x)
 51 {
 52   int now=root;
 53   while(1)
 54     {
 55       if(x<=sz[ch[now][0]]) now=ch[now][0];
 56       else
 57         {
 58           int tmp=sz[ch[now][0]]+1;
 59           if(x<=tmp) return now;
 60           x-=tmp; now=ch[now][1];
 61         }
 62     }
 63 }
 64 int Query(int ll,int rr)
 65 {
 66   int x=find(ll-1),y=find(rr+1);
 67   Splay(x,root); Splay(y,ch[root][1]);
 68   return ha[ch[y][0]];
 69 }
 70 int work(int x,int y)
 71 {
 72   int L=1,R=n-y,mid,res=0;
 73   while(L<=R)
 74     {
 75       mid=(L+R)/2;
 76       if(Query(x,x+mid-1)==Query(y,y+mid-1)) L=mid+1 ,res=mid;
 77       else R=mid-1;
 78     }
 79   return res;
 80 }
 81 void Insert(int x,int V)
 82 {
 83   int y=x+1; n++;
 84   x=find(x); y=find(y);
 85   Splay(x,root); Splay(y,ch[root][1]);
 86   CNT++; val[CNT]=V;
 87   fa[CNT]=y; ch[y][0]=CNT;
 88   Update(CNT); Update(y); Update(root);
 89 }
 90 int main()
 91 {
 92   scanf("%s",s+2); s[1]='a'-1;
 93   n=strlen(s+1)+1; s[n]='a'-1;
 94   scanf("%d",&m); v[0]=1;
 95   for(int i=1;i<MAXN;i++)v[i]=(LL)v[i-1]*27%mod;
 96   Build(root,1,n,0);
 97   while(m--)
 98     {
 99       scanf("%s",s);
100       if(s[0]=='Q')
101         {
102           int x,y; scanf("%d%d",&x,&y);
103           if(x>y) swap(x,y);
104           printf("%d\n",work(x+1,y+1));
105         }
106       else if(s[0]=='R')
107         {
108           int x; scanf("%d%s",&x,s);
109           x=find(x+1); Splay(x,root); 
110           val[x]=s[0]-'a'+1; Update(x);
111         }
112       else if(s[0]=='I')
113         {
114           int x; scanf("%d%s",&x,s); 
115           Insert(x+1,s[0]-'a'+1);
116         }
117     }
118 }

转载于:https://www.cnblogs.com/D-O-Time/p/8081634.html

  • 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、付费专栏及课程。

余额充值