2020 CCPC 威海 - G Caesar Cipher (线段树 + hash)

题意 :

给定一个数组 ,范围为 [0,65536),有以下两种操作:

  1. 给出 x , y 把 [x , y] 内的每个数 + 1 同时对 65536 取模。
  2. 给出 x,y,L , 查询区间 [x , x + L - 1] 和区间 [y , y + L - 1]是否完全相同。

思路

原文

思路就是 线段树维护 hash ,有区间修改和查询 判断两段 hash值是否相同就可以了。

首先考虑一下区间合并(也就是pushup)
线段树的每个节点表示这一段的 hash 值,在区间合并的操作时 大区间的 hash 值就是:
\(左区间的 hash值 * p[len] (len表示右区间的长度) + 右区间的 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;
}

posted @ 2022-10-19 19:46  kingwzun  阅读(42)  评论(0编辑  收藏  举报