1. 程式人生 > 實用技巧 >gym102798 Caesar Cipher (2020 China Collegiate Programming Contest, Weihai Site) 線段樹

gym102798 Caesar Cipher (2020 China Collegiate Programming Contest, Weihai Site) 線段樹

題意

有一個一個長度為n的陣列a和兩種操作(1代表對區間\([l,r]\)加1,2代表詢問區間\([x,x+L-1]\)和區間\([y,y+L-1]\)是否相同),其中操作1對65536取模。

思路

容易想到可以用雜湊判斷是否相等。然後題目中的陣列是對65536取模的,就會想到hash的模數取65536,即用unsigned short自然溢位取模,但是這個模數被出題人卡掉了。

另外,做字串雜湊時,雜湊的模數一般會選用大指數。因為當base和mod互質時,這個hash函式在\([0,mod)\)上每個值概率相等,此時單次比較的錯誤率為\(\frac1{mod}\)

對於65536取模的情況,與

codeforces438D相同,除了區間和外再加一個區間最大值,對於大於等於65536的區間暴力修改。

程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const ll base=1e8+7;
const int maxn=5e5+5;

struct Tree{
    ll max,sum,tag;
}tree[maxn*4];
ll a[maxn],p[maxn],pre[maxn];
int n,q;


ll M(ll val){
    return val>=mod?val-mod:val<0?val+mod:val;
}

void push_up(int i)
{
    tree[i].max=max(tree[i<<1].max,tree[i<<1|1].max);
    tree[i].sum=M(tree[i<<1].sum+tree[i<<1|1].sum);
}

void push_down(int i,int L,int R)
{
    if(tree[i].tag)
    {
        tree[i<<1].max+=tree[i].tag;
        tree[i<<1|1].max+=tree[i].tag;
        int mid=L+R>>1;
        if(L!=mid)
            tree[i<<1].tag+=tree[i].tag;
        if(mid+1!=R)
            tree[i<<1|1].tag+=tree[i].tag;
        tree[i<<1].sum=M(tree[i<<1].sum+M(pre[mid]-pre[L-1])*tree[i].tag%mod);
        tree[i<<1|1].sum=M(tree[i<<1|1].sum+M(pre[R]-pre[mid])*tree[i].tag%mod);
        tree[i].tag=0;
    }
}

void build(int i,int l,int r)
{
    if(l==r)
    {
        tree[i].max=a[l];
        tree[i].sum=a[l]*p[l]%mod;
        tree[i].tag=0;
        return;
    }
    int mid=l+r>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    push_up(i);
}

void add(int i,int l,int r,int L,int R)
{
    push_down(i,L,R);
    if(l==L && r==R)
    {
        if(l==r)
        {
            tree[i].max++;
            if(tree[i].max==65536)tree[i].max=0;
            tree[i].sum=tree[i].max*p[l]%mod;
            return;
        }
        else if(tree[i].max<65535)
        {            
            tree[i].max++;
            tree[i].tag++;
            tree[i].sum=M(tree[i].sum+M(pre[R]-pre[L-1]));
            return;
        }
    }
    int mid=L+R>>1;
    if(r<=mid)
        add(i<<1,l,r,L,mid);
    else if(l>mid)
        add(i<<1|1,l,r,mid+1,R);
    else
    {
        add(i<<1,l,mid,L,mid);
        add(i<<1|1,mid+1,r,mid+1,R);
    }
    push_up(i);
}

ll cal(int i,int l,int r,int L,int R)
{
    push_down(i,L,R);
    int mid=L+R>>1;
    if(l==L && r==R)return tree[i].sum;
    if(r<=mid)
        return cal(i<<1,l,r,L,mid);
    else if(l>mid)
        return cal(i<<1|1,l,r,mid+1,R);
    else
        return M(cal(i<<1,l,mid,L,mid)+cal(i<<1|1,mid+1,r,mid+1,R));
}

int main()
{
    ios::sync_with_stdio(false);

    cin>>n>>q;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    p[0]=1;
    pre[0]=1;
    for(int i=1;i<=n;i++)
    {
        p[i]=p[i-1]*base%mod;
        pre[i]=M(pre[i-1]+p[i]);
    }

    build(1,1,n);
    int op,l,r,x,y,L;
    while(q--)
    {
        cin>>op;
        if(op==1)
        {
            cin>>l>>r;
            add(1,l,r,1,n);
        }
        else
        {
            cin>>x>>y>>L;
            if(x>y)swap(x,y);
            ll res1=cal(1,x,x+L-1,1,n),res2=cal(1,y,y+L-1,1,n);
            if(res1*p[y-x]%mod==res2)
                cout<<"yes\n";
            else
                cout<<"no\n";
        }
        
    }

    return 0;
}