2020CCPC威海站 G Caesar Cipher (线段树维护哈希)

在这里插入图片描述
第一次做线段树维护哈希的题目 当做模板重点理解一下
题意:给定你n和q,然后给定你一个长度为n的序列,q次操作,每次操作有两种形式:
1 x y:代表从序列的第x项到第y项所有的(a[i] + 1) mod 65536
2 x y l:表示从询问分别以x和y为起点的两个序列,然后这两个序列是否完全相同,输出yes或no

思路:很明显我们看到区间操作[l,r]加一可以想到用线段树去操作,然后操作二是询问两个序列是否相等我们又可以想到是否可以用哈希去判断这两个序列是否是相等的。

具体实现:
hash数组也就是我们的建树数组,base数组是我们用来维护每个树节点的的位权的大小数组(他初始化为p数组对应节点),lazy数组就是区间更新的标记数组,ma数组是来存储原序列中最大的为多少的数组。说明一下ma数组的作用,因为这道题更新之后会对a数组有一个取余操作,但是我们的哈希过程是会取模的,所以他一定表示不是原序列的真实值,所以我们需要这个ma数组存一下,并且每次更新hash数组完成之后,紧跟一个更新的ma数组的函数,去看那些节点达到了65536这个值然后去更新它

然后下推延迟标记的函数:
因为树上的每一个节点都有它自己的位权,所以我们更新延迟标记的时候,需要把他们的权位对齐,也就是hash[rt<<1] = hash[rt<<1] + base[rt<<1]*lazy[rt];标记数组乘以这个点的位权。

查询和更新函数就都很正常了,终点注意一下取模的更新函数即可。

最后查询答案的时候,标号小的那个节点实际上是比大节点少乘了很多次位权的,所以要正确的比较他们两个的大小,还需要把他们的权值位对齐才可,就是work函数的实现

再就是这个题好像会卡unsigned long long的自然溢出,所以自己取一个模数即可。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define hash Hash
const int MAXN = 5e5 + 7;
const int P = 13331;
const ll MOD = 1e9 + 7;
ll a[MAXN],hash[MAXN<<2],lazy[MAXN<<2],base[MAXN<<2],p[MAXN],ma[MAXN<<2],n,q;
void init(){ p[0] = 1;for(int i = 1;i < MAXN;i ++) p[i] = p[i-1] * P % MOD; }
void pushup(int rt){ hash[rt] = (hash[rt<<1] + hash[rt<<1|1])%MOD; ma[rt] = max(ma[rt<<1],ma[rt<<1|1]); }
void pushdown(int rt){
	if(lazy[rt]){
		lazy[rt<<1] += lazy[rt];lazy[rt<<1|1] += lazy[rt];
		ma[rt<<1] += lazy[rt];ma[rt<<1|1] += lazy[rt];
		hash[rt<<1] = (hash[rt<<1] + lazy[rt]*base[rt<<1])%MOD;
		hash[rt<<1|1] = (hash[rt<<1|1] + lazy[rt]*base[rt<<1|1])%MOD;
		lazy[rt] = 0;
	}
}
void build(int rt,int l,int r){
	lazy[rt] = 0;
	if(l == r){ ma[rt] = a[l];base[rt] = p[l];hash[rt] = a[l]*base[rt]%MOD;return; }
	int mid = (l+r)>>1;
	build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);
	base[rt] = (base[rt<<1] + base[rt<<1|1]) % MOD;//别忘了这个底的更新
	pushup(rt);
}
void modify(int rt,int l,int r,int L,int R){
	if(l >= L && r <= R){ ma[rt]++;lazy[rt]++;hash[rt] = (hash[rt] + base[rt])%MOD;return; }
	pushdown(rt);
	int mid = (l+r)>>1;
	if(L <= mid) modify(rt<<1,l,mid,L,R); if(R > mid) modify(rt<<1|1,mid+1,r,L,R);
	pushup(rt);
}
void modify_mod(int rt,int l,int r,int L,int R){
	if(ma[rt] < 65536) return ;
	if(l == r){ ma[rt] %= 65536;hash[rt] = (ma[rt]*base[rt])%MOD;lazy[rt] = 0;return ; }
	pushdown(rt);
	int mid = (l+r)>>1;
	if(L <= mid) modify_mod(rt<<1,l,mid,L,R); if(R > mid) modify_mod(rt<<1|1,mid+1,r,L,R);
	pushup(rt);
}
ll query(int rt,int l,int r,int L,int R){
	if(l >= L && r <= R) return hash[rt];
	pushdown(rt);
	ll ans = 0;int mid = (l+r)>>1;
	if(L <= mid) ans = (ans + query(rt<<1,l,mid,L,R))%MOD;
	if(R > mid) ans = (ans + query(rt<<1|1,mid+1,r,L,R))%MOD;
	return ans;
}
void work(int x,int y,int len){
	if(x > y) swap(x,y);
	ll t1 = query(1,1,n,x,x+len-1)%MOD,t2 = query(1,1,n,y,y+len-1)%MOD;
	if(x < y) t1 = (t1*p[y-x])%MOD;
	printf(t1 == t2?"yes\n":"no\n");
}
int main()
{
	init();
	scanf("%d%d",&n,&q);
	for(int i = 1;i <= n;i ++) scanf("%lld",&a[i]);
	build(1,1,n);
	int op,x,y,len;
	while(q--){
		scanf("%d",&op);
		if(op == 1){ scanf("%d%d",&x,&y);modify(1,1,n,x,y);modify_mod(1,1,n,x,y); }
		else{ scanf("%d%d%d",&x,&y,&len);work(x,y,len); }
	}
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值