题目链接
题意:
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
#include
#include
#include
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相等的数再mod了1e9+7后就不相等了,下面是调试用的代码,可以尝试一下:
#include
#include
#include
#include
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<
我们可以输入以下样例:
4
65536 65536 65536 1
4
0 0 0 1
看两个数相不相等,本来是应该相等的,但实际输出不相等:
25525
1
所以,我们不能用这个mod,就应该直接找到65536然后将他变为0,然后一步一步的求哈希值。这样就可以保证了mod的正确性
下面是AC代码:
#include
#include
#include
#include
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的两条性质是正确的
上一篇:【C++】模板初阶
下一篇:DDoS报告团伙规模