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 值就是:
代码就是:
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; }
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/16804980.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步