G Caesar Cipher
题目大意:
给你一个长度为n的序列,接下去有q种操作。
1.给你两个数l , r。区间[ l , r ]内的数都+1并对65536取模。
2.查询两段区间[x,x+len-1]和[y,y+len-1]内的序列是否相同。
思路:
第一种操作可以转化为区间更新,第二种操作中判断是否相等可以用哈希来判断,然后加上区间查询即可。那么就想到用线段树去维护每个位置的加权哈希值,这样就可以轻松更新和访问了。
但是取模该怎么处理呢。我们称一个数字从65535到65536为溢出,因为整个序列的长度为n,总共的操作次数为m,所以最多会出现n*m/65536次的溢出,估算一下是5e6级别的,对于这些溢出,我们可以写一个函数单独处理。
AC Code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=5e5+10,mod=1e9+7;
const int base=2333333;
LL a[N],f[N];
struct node
{
int l,r;
LL Hash,lazy,base,w;
}tr[N*4];
void pushup(int k)
{
tr[k].Hash=(tr[k<<1].Hash+tr[k<<1|1].Hash)%mod;
tr[k].w=max(tr[k<<1].w,tr[k<<1|1].w);
}
void pushdown(int k)
{
if(tr[k].l==tr[k].r)
{
tr[k].lazy=0;
return ;
}
if(tr[k].lazy)
{
tr[k<<1].Hash=(tr[k<<1].Hash+tr[k].lazy*tr[k<<1].base%mod)%mod;
tr[k<<1|1].Hash=(tr[k<<1|1].Hash+tr[k].lazy*tr[k<<1|1].base%mod)%mod;
tr[k<<1].lazy+=tr[k].lazy;
tr[k<<1|1].lazy+=tr[k].lazy;
tr[k<<1].w+=tr[k].lazy;
tr[k<<1|1].w+=tr[k].lazy;
tr[k].lazy=0;
}
}
void build(int k,int l,int r)
{
tr[k].l=l,tr[k].r=r;
tr[k].lazy=0;
if(l==r)
{
tr[k].Hash=a[l]*f[l]%mod;
tr[k].base=f[l];
tr[k].w=a[l];
return ;
}
int mid=(tr[k].l+tr[k].r)/2;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
tr[k].base=(tr[k<<1].base+tr[k<<1|1].base)%mod;
pushup(k);
}
void modify(int k,int l,int r) //区间修改
{
if(tr[k].l>=l&&tr[k].r<=r)
{
tr[k].lazy++;
tr[k].w++;
tr[k].Hash=(tr[k].Hash+tr[k].base)%mod;
return ;
}
pushdown(k);
int mid=(tr[k].l+tr[k].r)/2;
if(l<=mid) modify(k<<1,l,r);
if(r>mid) modify(k<<1|1,l,r);
pushup(k);
}
void modify_mod(int k)
{
if(tr[k].w<65536) return ;
if(tr[k].l==tr[k].r)
{
tr[k].Hash=0;
tr[k].w=0;
return ;
}
pushdown(k);
modify_mod(k<<1);
modify_mod(k<<1|1);
pushup(k);
}
LL query(int k,int l,int r)
{
if(tr[k].l>=l&&tr[k].r<=r)
return tr[k].Hash;
if(tr[k].lazy) pushdown(k);
int mid=(tr[k].l+tr[k].r)/2;
LL ans=0;
if(l<=mid) ans=query(k<<1,l,r);
if(r>mid) ans=(ans+query(k<<1|1,l,r))%mod;
return ans;
}
bool check(int x,int y,int L) //两个起点和长度
{
if(x>y) swap(x,y);
LL ans1=query(1,x,x+L-1)*f[y-x]%mod;
LL ans2=query(1,y,y+L-1);
if(ans1==ans2) return true;
else return false;
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
f[0]=1;
for(int i=1;i<=n;i++) //为每一个位置分配哈希值
f[i]=f[i-1]*base%mod;
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(1,1,n); //建立线段树
while(q--)
{
int op; //哪种类型的操作
scanf("%d",&op);
if(op==1) //区间修改
{
int l,r;
scanf("%d%d",&l,&r);
modify(1,l,r);
modify_mod(1);
}
else
{
int x,y,L;
scanf("%d%d%d",&x,&y,&L);
if(check(x,y,L)) printf("yes\n");
else printf("no\n");
}
}
//system("pause");
return 0;
}