2020 CCPC 威海 - G Caesar Cipher (线段树 + hash)

本文介绍了一种结合线段树和Hash算法的数据结构解决方案,用于处理数组的区间修改及区间相似性查询问题。通过使用线段树维护Hash值,实现了高效地区间更新和判断两个区间元素是否完全相同的功能。

链接 : G Caesar Cipher

题意 :
给定一个数组 ,范围为 [0,65536),有以下两种操作:

  1. 给出 x , y 把 [x , y] 内的每个数 + 1 同时对 65536 取模。
  2. 给出 x,y,L , 查询区间 [x , x + L - 1] 和区间 [y , y + L - 1]是否完全相同。

思路 :

  1. 思路就是 线段树维护 hash ,有区间修改和查询 判断两段 hash值是否相同就可以了。
  2. 首先考虑一下区间合并(也就是pushup),线段树的每个节点表示这一段的 hash 值,在区间合并的操作时 大区间的 hash 值就是 左区间的 hash值 * base ^ len (len表示右区间的长度) + 右区间的 hash值 。Hash[rt] = (Hash[rt << 1] * poww[r - mid] + Hash[rt << 1 | 1])
  3. 然后是区间更新 ,把这个区间的值全部 + 1, hash 的变化 就是 base的前缀和 ,例如 某一个区间的hash值为 ∑ i = 0 n \sum_{i=0}^n i=0na[i] * base ^ i (n 为区间长度 - 1),那如果现在把每个 a[i] 都 + 1 , 那hash值的变化就是 ∑ i = 0 n \sum_{i=0}^n i=0n base ^ i , 这里用个前缀和记录一下 ,就可以很好的用 lazy维护。
  4. 查询操作和普通的查询不一样 , 因为在合并两个区间时 ,合并后的 hash 值 不是两个 hash的 简单相加(参考上面的pushup) , 也就是左区间的 hash值要先乘上 base ^ len(len为右区间长度) 再加右区间。
  5. 最后就要考虑一下溢出的问题了,如果在更新过程 某个数 >= 65536 , 就要对 65536 取模了 ,直接在更新操作里判断=肯定不好写 ,所以我们在每次更新后都找一下有没有数 大于 65536 ,这里怎么找呢 ,肯定不能暴力扫一遍 。 可以利用线段树进行一个类似二分的过程 ,维护一下每个区间的最大值 ,如果左区间最大值大于 65536 ,继续更新左区间,这样一直下去找到那个值为止 ,复杂度 log(n) ,不用担心超时。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
const int mod = 65536;
const int mod1 = 1e9 + 7;
const int mod2 = 998244353;
const int r = 31;
const int maxn = 2e6 + 7;
ll Hash[maxn],ma[maxn],la[maxn];
ll pre[maxn],poww[maxn];   // base 的幂次     前缀和
int a[maxn] ,n,q,x,y,op,L;
void pushup
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值