2020 CCPC 威海 - G Caesar Cipher (线段树 + hash)
题意 :
给定一个数组 ,范围为 [0,65536),有以下两种操作:
- 给出 x , y 把 [x , y] 内的每个数 + 1 同时对 65536 取模。
- 给出 x,y,L , 查询区间 [x , x + L - 1] 和区间 [y , y + L - 1]是否完全相同。
思路
思路就是 线段树维护 hash ,有区间修改和查询 判断两段 hash值是否相同就可以了。
首先考虑一下区间合并(也就是pushup)
线段树的每个节点表示这一段的 hash 值,在区间合并的操作时 大区间的 hash 值就是:
\(左区间的 hash值 * p[len] (len表示右区间的长度) + 右区间的 hash值\)
代码就是:
int len=tr[u<<1|1].r-tr[u<<1|1].l+1;//找到右边的间距
tr[u].has= (tr[u<<1].has*p[len]+ tr[u<<1|1].has)%MOD1;//计算当前的hash值
然后是区间更新
长度为len的区间的值全部 + 1, hash 的变化就是
区间哈希值 加上 长度为len,每个值为1 的序列哈希后的值。
查询操作
哈希值参考上面的pushup
最后就要考虑一下溢出的问题
按照题意如果在更新过程 某个数 >= 65536 , 就要对 65536 取模。
我们维护一下每个区间的最大值,在每次更新后都找一下有没有数 大于 65536,如果没有就break。
溢出判断复杂度:
每次判断是 log(n),
又因为数值的增长是每次是1,也就是说每6e4次才会特判一次,也就是说最多只会特判5e5/6e4大约是10次。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
const int N=5e5+10;
const int MOD1=1e9+7;
int h[N], p[N],h1[N],P=13331; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
// 初始化
int a[N],n,m;
void init()
{
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
// h[i] = (h[i - 1] * P + a[i])%MOD1;
h1[i]=(h1[i-1] * P + 1)%MOD1;
p[i] = (p[i - 1] * P)%MOD1;
}
}
struct node
{
int l,r;
int has;
int lazy;//记录加上了多少
int maxn;
} tr[N*4];
void pushup(int u)
{
tr[u].maxn=max(tr[u<<1].maxn,tr[u<<1|1].maxn);
int len=tr[u<<1|1].r-tr[u<<1|1].l+1;//找到右边的间距
tr[u].has= (tr[u<<1].has*p[len]+ tr[u<<1|1].has)%MOD1;//计算当前的hash值
}
void pushdown(int u,int lazy)
{
int len=tr[u].r-tr[u].l+1;
int key=h1[len]*lazy%MOD1;//找到关键值
tr[u].has=(tr[u].has+key)%MOD1;
tr[u].maxn+=lazy;
tr[u].lazy+=lazy;
}
void pushdown(int u)
{
if(tr[u].lazy)
{
pushdown(u<<1,tr[u].lazy);
pushdown(u<<1|1,tr[u].lazy);
tr[u].lazy=0;
}
}
void build(int u,int l,int r)
{
tr[u].l = l, tr[u].r = r;
if(l==r)
{
tr[u].maxn=a[l];
tr[u].has=a[l];
tr[u].lazy=0;
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int l,int r)//[l,r]+1;
{
if(tr[u].l>=l&&tr[u].r<=r)
{
pushdown(u,1);
}
else{
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r);
if(r>mid) modify(u<<1|1,l,r);
pushup(u);
}
}
void modify_mod(int u){
if(tr[u].maxn<65536) return ;
if(tr[u].l==tr[u].r){
tr[u].maxn=0;
tr[u].has=0;
return;
}
pushdown(u);
modify_mod(u<<1);
modify_mod(u<<1|1);
pushup(u);
}
int query(int u,int l,int r)
{
if(tr[u].l>=l&&tr[u].r<=r) return tr[u].has;
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
int s1=0;
int s2=0;
if(r>mid) s1=query(u<<1|1,l,r);
if(l<=mid) s2=query(u<<1,l,r);
int len=max(0ll,min(r,tr[u].r)-mid);
s2=s2*p[len]%MOD1;
s1=(s1+s2)%MOD1;
return s1;
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(int i=1; i<=n; i++)
scanf("%lld",&a[i]);
init();
build(1,1,n);
while(m--)
{
int op,l,r;
cin>>op>>l>>r;
if(op==1){
modify(1,l,r);
modify_mod(1);
}
if(op==2)
{
int Len;
cin>>Len;
int ha1=query(1,l,l+Len-1);
int ha2=query(1,r,r+Len-1);
if(ha1==ha2) printf("yes\n");
else printf("no\n");
}
}
return 0;
}