G - Caesar Cipher Gym - 102798G

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了G - Caesar Cipher Gym - 102798G相关的知识,希望对你有一定的参考价值。

G - Caesar Cipher Gym - 102798G

题意:

对于一个区间,有两种操作,一种是对区间每个数+1再mod65536,另一种则是判断两个区间的数是否相同

题解:

参考题解:
很明显线段树,但是判断两个区间的数是否相同用什么?我们可以用hash
用线段树维护hash,区间修改和查询,判断两端hash是否相同
单hash模板如下:

for (int i=1;i<=len;i++)
	ans=ans*base+(ull)s[i];

现在我们看hash如何与线段树操作配套使用:

  1. 区间合并(pushup),线段树的每个节点表示这一段的hash值,区间合并时,大区间的hash = 左区间hash与右区间hash合并,我们知道hash的相邻为是以base为倍数的,所以合并时左区间的hash要乘baselen(len表示右区间的长度)+右区间的hash
    Hash[rt] = (Hash[rt << 1] * poww[r - mid] + Hash[rt << 1 | 1])
    base的倍数我们提前预处理
    你可以理解成12和34拼接成四位数,要12*102+34=1234
  2. 区间更新,要将一段区间都+1,我们要先知道hash等于什么,hash= ∑ i = 0 n a i ∗ b a s e i \\sum_{i=0}^n a_i * base^i i=0naibasei,n=区间长度-1,现在我们将a[i]+=1,那么hash比原本多了 ∑ i = 0 n b a s e i \\sum_{i=0}^n base^i i=0nbasei,这里我们用前缀和记录,可以用lazy维护
  3. 查询操作和第一个区间合并也是类似,不是两个hash简单的合并,而且左区间的 hash值要先乘上 base ^ len(len为右区间长度) 再加右区间。
  4. 最后还要对65536取模,我们每次更新后用线段树维护每个区间的最大值,如果左区间最大值大于65536,继续更新左区间,复杂度log(n)

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
const int mod = 65536;
const int mod1 = 1e9 + 7;
const int base = 31;
const int maxn = 2e6 + 7;
ll Hash[maxn],ma[maxn],lazy[maxn];
//ma数组用来判断是否超出mod 
ll pre[maxn],poww[maxn];   // r 的幂次     前缀和
int a[maxn] ,n,q,x,y,op,L;
void pushup(int l , int r, int rt){
    int mid = (l + r) / 2;
    Hash[rt] = (Hash[rt << 1] * poww[r - mid] % mod1 + Hash[rt << 1 | 1]) % mod1;
    ma[rt] = max(ma[rt << 1] , ma[rt << 1 | 1]);
}
void pushdown(int l,int r,int rt){
    if(lazy[rt] == 0) return ;
    int  mid = (l + r) / 2;
    
    Hash[rt << 1] = (Hash[rt << 1] + lazy[rt] *  pre[mid - l] % mod1) % mod1;     //加上前缀和的 幂次
    Hash[rt << 1 | 1] = (Hash[rt << 1 | 1] + lazy[rt] * pre[r - mid - 1] % mod1) % mod1;

    ma[rt << 1] += lazy[rt];
    ma[rt << 1|1] += lazy[rt];


    lazy[rt<<1] += lazy[rt];
    lazy[rt<<1|1] += lazy[rt];
    lazy[rt] = 0;
}
void update(int L,int R,int l,int r,int rt){
    if(L <= l && R >= r){
        Hash[rt] = (Hash[rt] + pre[r - l]) % mod1;
        lazy[rt] ++;
        ma[rt] ++;
        return ;
	}
	pushdown(l , r, rt);
	int mid = (l + r) / 2;
	if(R > mid) update(L , R ,mid + 1 ,r ,rt << 1 | 1);
	if(L <= mid) update(L ,R ,l , mid , rt << 1);
	pushup(l ,r , rt);
}
void update_mod(int l,int r,int rt){  //考虑溢出
    if(ma[rt] < mod){                 //没有超过 mod的 直接退出
        return ;
    }
    if(l == r){
        ma[rt] -= mod;
        Hash[rt] -= mod;
        return ;
    }
    pushdown(l , r, rt);
    int mid = (l + r) / 2;
    if(ma[rt << 1] >= mod) update_mod( l , mid ,rt << 1);
    if(ma[rt << 1 | 1] >= mod) update_mod(mid + 1 , r, rt << 1 | 1);
    pushup(l , r, rt);

}
ll query(int L,int R,int l,int r,int rt){
	ll s = 0;
	if(L <= l && R >= r){
        return Hash[rt];
	}
	pushdown(l , r, rt);
	int mid = (l + r) / 2;
	if(R > mid)  s = (s + query(L,R,mid + 1,r,rt<<1|1) ) % mod1;
	if(L <= mid) s = (s + poww[max(0,min(R , r)- mid)] * query(L,R,l,mid,rt<<1) % mod1) % mod1;
	return s;
}
void build(int l,int r ,int rt){
    if(l == r){
        Hash[rt] = a[l];
        ma[rt] = a[l];
        return ;
    }
    int mid = (l + r) / 2;
    build(l , mid ,rt << 1);
    build(mid + 1 , r,rt << 1 | 1);
    pushup(l,r, rt);
}
int main (){
    poww[0] = pre[0] = 1;
    for(int i = 1; i <= 5e5 ; i ++){
        poww[i] = poww[i-1] * base % mod1;
    }
    //poww[i]=base^i 
    for(int i = 1; i <= 5e5 ; i ++){
    	pre[i] =  (pre[i-1] + poww[i]) % mod1;
    }
    //pre[r-l]=Σ(l+1->r)base^i
    scanf("%d%d",&n,&q);
    for(int i = 1; i<= n; i ++){
        scanf("%d",&a[i]);
    }
    build(1 , n,1);
    while(q--){
        scanf("%d",&op);
        if(op == 1){
            scanf("%d%d",&x,&y);
            update(x,y,1,n,1);
            update_mod(1 , n , 1);
        }
        if(op == 2){
            scanf("%d%d%d",&x,&y,&L);
            ll h1 = query(x , x + L - 1 ,1 , n, 1);
            ll h2 = query(y , y + L - 1 ,1 , n, 1);
            if(h1 == h2 ) printf ("yes\\n");
            else printf ("no\\n");
        }
    }
}


以上是关于G - Caesar Cipher Gym - 102798G的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces - 102222C - Caesar Cipher

Caesar Cipher

水平输出包含空格的转换文本(凯撒密码)

java cipher

一直以来任何英语中cypher和cipher意思完全一样吗?

crypto node createcipher 对应java啥方法