概述
G - Caesar Cipher Gym - 102798G
题意:
对于一个区间,有两种操作,一种是对区间每个数+1再mod65536,另一种则是判断两个区间的数是否相同
题解:
参考题解:
很明显线段树,但是判断两个区间的数是否相同用什么?我们可以用hash
用线段树维护hash,区间修改和查询,判断两端hash是否相同
单hash模板如下:
for (int i=1;i<=len;i++)
ans=ans*base+(ull)s[i];
现在我们看hash如何与线段树操作配套使用:
- 区间合并(pushup),线段树的每个节点表示这一段的hash值,区间合并时,大区间的hash = 左区间hash与右区间hash合并,我们知道hash的相邻为是以base为倍数的,所以合并时左区间的hash要乘baselen(len表示右区间的长度)+右区间的hash
Hash[rt] = (Hash[rt << 1] * poww[r - mid] + Hash[rt << 1 | 1])
base的倍数我们提前预处理
你可以理解成12和34拼接成四位数,要12*102+34=1234 - 区间更新,要将一段区间都+1,我们要先知道hash等于什么,hash= ∑ i = 0 n a i ∗ b a s e i sum_{i=0}^n a_i * base^i ∑i=0nai∗basei,n=区间长度-1,现在我们将a[i]+=1,那么hash比原本多了 ∑ i = 0 n b a s e i sum_{i=0}^n base^i ∑i=0nbasei,这里我们用前缀和记录,可以用lazy维护
- 查询操作和第一个区间合并也是类似,不是两个hash简单的合并,而且左区间的 hash值要先乘上 base ^ len(len为右区间长度) 再加右区间。
- 最后还要对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 base = 31;
const int maxn = 2e6 + 7;
ll Hash[maxn],ma[maxn],lazy[maxn];
//ma数组用来判断是否超出mod
ll pre[maxn],poww[maxn]; // r 的幂次 前缀和
int a[maxn] ,n,q,x,y,op,L;
void pushup(int l , int r, int rt){
int mid = (l + r) / 2;
Hash[rt] = (Hash[rt << 1] * poww[r - mid] % mod1 + Hash[rt << 1 | 1]) % mod1;
ma[rt] = max(ma[rt << 1] , ma[rt << 1 | 1]);
}
void pushdown(int l,int r,int rt){
if(lazy[rt] == 0) return ;
int mid = (l + r) / 2;
Hash[rt << 1] = (Hash[rt << 1] + lazy[rt] * pre[mid - l] % mod1) % mod1; //加上前缀和的 幂次
Hash[rt << 1 | 1] = (Hash[rt << 1 | 1] + lazy[rt] * pre[r - mid - 1] % mod1) % mod1;
ma[rt << 1] += lazy[rt];
ma[rt << 1|1] += lazy[rt];
lazy[rt<<1] += lazy[rt];
lazy[rt<<1|1] += lazy[rt];
lazy[rt] = 0;
}
void update(int L,int R,int l,int r,int rt){
if(L <= l && R >= r){
Hash[rt] = (Hash[rt] + pre[r - l]) % mod1;
lazy[rt] ++;
ma[rt] ++;
return ;
}
pushdown(l , r, rt);
int mid = (l + r) / 2;
if(R > mid) update(L , R ,mid + 1 ,r ,rt << 1 | 1);
if(L <= mid) update(L ,R ,l , mid , rt << 1);
pushup(l ,r , rt);
}
void update_mod(int l,int r,int rt){ //考虑溢出
if(ma[rt] < mod){ //没有超过 mod的 直接退出
return ;
}
if(l == r){
ma[rt] -= mod;
Hash[rt] -= mod;
return ;
}
pushdown(l , r, rt);
int mid = (l + r) / 2;
if(ma[rt << 1] >= mod) update_mod( l , mid ,rt << 1);
if(ma[rt << 1 | 1] >= mod) update_mod(mid + 1 , r, rt << 1 | 1);
pushup(l , r, rt);
}
ll query(int L,int R,int l,int r,int rt){
ll s = 0;
if(L <= l && R >= r){
return Hash[rt];
}
pushdown(l , r, rt);
int mid = (l + r) / 2;
if(R > mid) s = (s + query(L,R,mid + 1,r,rt<<1|1) ) % mod1;
if(L <= mid) s = (s + poww[max(0,min(R , r)- mid)] * query(L,R,l,mid,rt<<1) % mod1) % mod1;
return s;
}
void build(int l,int r ,int rt){
if(l == r){
Hash[rt] = a[l];
ma[rt] = a[l];
return ;
}
int mid = (l + r) / 2;
build(l , mid ,rt << 1);
build(mid + 1 , r,rt << 1 | 1);
pushup(l,r, rt);
}
int main (){
poww[0] = pre[0] = 1;
for(int i = 1; i <= 5e5 ; i ++){
poww[i] = poww[i-1] * base % mod1;
}
//poww[i]=base^i
for(int i = 1; i <= 5e5 ; i ++){
pre[i] = (pre[i-1] + poww[i]) % mod1;
}
//pre[r-l]=Σ(l+1->r)base^i
scanf("%d%d",&n,&q);
for(int i = 1; i<= n; i ++){
scanf("%d",&a[i]);
}
build(1 , n,1);
while(q--){
scanf("%d",&op);
if(op == 1){
scanf("%d%d",&x,&y);
update(x,y,1,n,1);
update_mod(1 , n , 1);
}
if(op == 2){
scanf("%d%d%d",&x,&y,&L);
ll h1 = query(x , x + L - 1 ,1 , n, 1);
ll h2 = query(y , y + L - 1 ,1 , n, 1);
if(h1 == h2 ) printf ("yesn");
else printf ("non");
}
}
}
最后
以上就是柔弱冥王星为你收集整理的G - Caesar Cipher Gym - 102798G的全部内容,希望文章能够帮你解决G - Caesar Cipher Gym - 102798G所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复