2020 CCPC(威海)—G Caesar Cipher(哈希+线段树)

G Caesar Cipher

题目大意:

给你一个长度为n的序列,接下去有q种操作。
1.给你两个数l , r。区间[ l , r ]内的数都+1并对65536取模。
2.查询两段区间[x,x+len-1]和[y,y+len-1]内的序列是否相同。

思路:

第一种操作可以转化为区间更新,第二种操作中判断是否相等可以用哈希来判断,然后加上区间查询即可。那么就想到用线段树去维护每个位置的加权哈希值,这样就可以轻松更新和访问了。

但是取模该怎么处理呢。我们称一个数字从65535到65536为溢出,因为整个序列的长度为n,总共的操作次数为m,所以最多会出现n*m/65536次的溢出,估算一下是5e6级别的,对于这些溢出,我们可以写一个函数单独处理。

AC Code

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=5e5+10,mod=1e9+7;
const int base=2333333; 
LL a[N],f[N];
struct node
{
    
    
    int l,r;
    LL Hash,lazy,base,w;
}tr[N*4];
void pushup(int k)
{
    
    
    tr[k].Hash=(tr[k<<1].Hash+tr[k<<1|1].Hash)%mod;
    tr[k].w=max(tr[k<<1].w,tr[k<<1|1].w);
}
void pushdown(int k)
{
    
    
    if(tr[k].l==tr[k].r)
    {
    
    
        tr[k].lazy=0;
        return ;
    }
    if(tr[k].lazy)
    {
    
    
        tr[k<<1].Hash=(tr[k<<1].Hash+tr[k].lazy*tr[k<<1].base%mod)%mod;
        tr[k<<1|1].Hash=(tr[k<<1|1].Hash+tr[k].lazy*tr[k<<1|1].base%mod)%mod;
        tr[k<<1].lazy+=tr[k].lazy;
        tr[k<<1|1].lazy+=tr[k].lazy;
        tr[k<<1].w+=tr[k].lazy;
        tr[k<<1|1].w+=tr[k].lazy;
        tr[k].lazy=0;
    }
}
void build(int k,int l,int r)
{
    
    
    tr[k].l=l,tr[k].r=r;
    tr[k].lazy=0;
    if(l==r)
    {
    
    
        tr[k].Hash=a[l]*f[l]%mod;
        tr[k].base=f[l];
        tr[k].w=a[l];
        return ;
    }
    int mid=(tr[k].l+tr[k].r)/2;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    tr[k].base=(tr[k<<1].base+tr[k<<1|1].base)%mod;
    pushup(k);
}
void modify(int k,int l,int r)    //区间修改
{
    
    
    if(tr[k].l>=l&&tr[k].r<=r)
    {
    
    
        tr[k].lazy++;
        tr[k].w++;
        tr[k].Hash=(tr[k].Hash+tr[k].base)%mod;
        return ;
    }
    pushdown(k);
    int mid=(tr[k].l+tr[k].r)/2;
    if(l<=mid) modify(k<<1,l,r);
    if(r>mid) modify(k<<1|1,l,r);
    pushup(k);
}
void modify_mod(int k)
{
    
    
    if(tr[k].w<65536) return ;
    if(tr[k].l==tr[k].r)
    {
    
    
        tr[k].Hash=0;
        tr[k].w=0;
        return ;
    }
    pushdown(k);
    modify_mod(k<<1);
    modify_mod(k<<1|1);
    pushup(k);
}
LL query(int k,int l,int r)
{
    
    
    if(tr[k].l>=l&&tr[k].r<=r)
        return tr[k].Hash;
    if(tr[k].lazy) pushdown(k);
    int mid=(tr[k].l+tr[k].r)/2;
    LL ans=0;
    if(l<=mid) ans=query(k<<1,l,r);
    if(r>mid) ans=(ans+query(k<<1|1,l,r))%mod;
    return ans;
}
bool check(int x,int y,int L)   //两个起点和长度
{
    
    
    if(x>y) swap(x,y);
    LL ans1=query(1,x,x+L-1)*f[y-x]%mod;
    LL ans2=query(1,y,y+L-1);
    if(ans1==ans2) return true;
    else return false;
}
int main()
{
    
    
    int n,q;
    scanf("%d%d",&n,&q);
    f[0]=1;
    for(int i=1;i<=n;i++)   //为每一个位置分配哈希值
        f[i]=f[i-1]*base%mod;
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    build(1,1,n);   //建立线段树
    while(q--)
    {
    
    
        int op; //哪种类型的操作
        scanf("%d",&op);
        if(op==1)   //区间修改
        {
    
    
            int l,r;
            scanf("%d%d",&l,&r);
            modify(1,l,r);
            modify_mod(1);
        }
        else
        {
    
    
            int x,y,L;
            scanf("%d%d%d",&x,&y,&L);
            if(check(x,y,L)) printf("yes\n");
            else printf("no\n");
        }
    }
    //system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Stevenwuxu/article/details/113033190