题目链接
题意:
1:1 l r表示对[l,r]内的数+1%65536
2:2 l r L 表示[l,l+L-1] 和 [r,r+L-1] 是否一致
题解:
1:线段树维护哈希
2:
哈希的性质:
1:我们对于整段+x,对应的哈希值实际上就是加上了 x*
(相同长度下均为1的数组的哈希值)
2:我们在对应的mod下,以上的操作对应的数加1%mod仍然成立
如:1 2 65535 整体加1 即2 3 65536的哈希值%mod 后与 2 3 0 是一样。
首先上面的结论是正确的,所以我们可以就不用管哪些mod为0的情况,直接算就行,然后就有了下面WA9的代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=5e5+10,mod=65536;
typedef unsigned long long ULL;
ULL h[N], p[N],h1[N],P=131; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
// 初始化
int a[N],cop[N],n,m;
void init()
{
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
h[i] = (h[i - 1] * P + a[i])%mod;
h1[i]=(h1[i-1] * P + cop[i])%mod;
p[i] = (p[i - 1] * P)%mod;
}
}
// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
ULL get1(int l,int r)
{
return h1[r] - h1[l - 1] * p[r - l + 1];
}
struct node
{
int l,r;
ULL has;
int lazy;//记录加上了多少
} tr[N*4];
void pushup(int u)
{
int len=tr[u<<1|1].r-tr[u<<1|1].l+1;//找到右边的间距
tr[u].has=tr[u<<1].has*p[len]%mod;
tr[u].has+=tr[u<<1|1].has;//计算当前的hash值
tr[u].has%=mod;//一步一模
}
void pushdown(int u,int lazy)
{
ULL key=get1(tr[u].l,tr[u].r)*lazy;//找到关键值
key%=mod;
tr[u].has+=key;
tr[u].has%=mod;
tr[u].lazy+=lazy;
tr[u].lazy%=mod;
}
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,r,0,0};
if(l==r)
{
tr[u].lazy=0;
tr[u].has=get(l,l);
tr[u].has%=mod;
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);
return;
}
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);
}
node query(int u,int l,int r)
{
if(tr[u].l>=l&&tr[u].r<=r) return tr[u];
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
node res1= {0x3f3f3f3f,0,0,0},res2= {0x3f3f3f3f,0,0,0};
if(l<=mid) res1=query(u<<1,l,r);
if(r>mid) res2=query(u<<1|1,l,r);
node res;
if(res1.l==0x3f3f3f3f) res=res2;
else if(res2.l==0x3f3f3f3f) res=res1;
else
{
res.l=min(res1.l,res2.l);
res.r=max(res1.r,res2.r);
int len=res2.r-res2.l+1;
res.has=res1.has*p[len]%mod;
res.has+=res2.has;
res.has%=mod;
}
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) scanf("%d",&a[i]),cop[i]=1;
init();
build(1,1,n);
while(m--)
{
int op,l,r;
cin>>op>>l>>r;
if(op==1)
{
modify(1,l,r);
}
if(op==2)
{
int L;
cin>>L;
node ha1=query(1,l,l+L-1);
node ha2=query(1,r,r+L-1);
//printf("%llu %llu\n",ha1.has,ha2.has);
if(ha1.has==ha2.has) printf("yes\n");
else printf("no\n");
}
}
return 0;
}
开始以为这个WA9的代码是某一步mod的问题,改了好久,还是不对.后来才知道,这个题好像可能会卡ULL,所以我们需要用一个质数来mod(例如1e9+7等),但是我们本来只用mod65536相等的数模了1e9+7后再模65536就不相等了,下面是调试用的代码,可以尝试一下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=5e5+10,mod=65536,MOD=1e9+7;
#define int long long
typedef unsigned long long ULL;
int h[N], p[N],h1[N],P=13331; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
// 初始化
int a[N],cop[N],n,m;
void init()
{
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
h[i] = (h[i - 1] * P + a[i]);
h1[i]=(h1[i-1] * P + cop[i]);
p[i] = p[i - 1] * P;
}
}
// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
signed main()
{
while(cin>>n)
{
for(int i=1;i<=n;i++) cin>>a[i];
init();
cout<<get(1,n)%(1000000007)%65535<<endl;
}
return 0;
}
我们可以输入以下样例:
4
65536 65536 65536 1
4
0 0 0 1
看两个数相不相等,本来是应该相等的,但实际输出不相等:
25525
1
所以,我们不能用这个mod,就应该直接找到65536然后将他变为0,然后一步一步的求哈希值。这样就可以保证了mod的正确性
下面是AC代码:
#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;
}
就是加了一个modify_mod和多加了一个维护的maxn,主要就是记录区间的最大值,这里的作用主要是对于那个区间内的最大值为65536的数进行再次修改.
总结:
1:hash尽量不用ULL,可以用一个质数去mod
2:就是总结的以的hash的两条性质是正确的