G - Caesar Cipher Gym - 102798G

题目链接
题意:
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<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
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相等的数模了1e9+7后再模65536就不相等了,下面是调试用的代码,可以尝试一下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
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<<get(1,n)%(1000000007)%65535<<endl;
    }
    return 0;
}

我们可以输入以下样例:
4
65536 65536 65536 1
4
0 0 0 1
看两个数相不相等,本来是应该相等的,但实际输出不相等:
25525
1
所以,我们不能用这个mod,就应该直接找到65536然后将他变为0,然后一步一步的求哈希值。这样就可以保证了mod的正确性
下面是AC代码:

#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;
}

就是加了一个modify_mod和多加了一个维护的maxn,主要就是记录区间的最大值,这里的作用主要是对于那个区间内的最大值为65536的数进行再次修改.

总结:
1:hash尽量不用ULL,可以用一个质数去mod
2:就是总结的以的hash的两条性质是正确的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值