线段树+哈希【CF580E】Kefa and Watch

Description

\(n\)个数的字符串,\(m + k\)个操作

1 l r k把\(l - r\)赋值为\(k\)

2 l r d询问\(l - r\)是否有长度为\(d\)的循环节

\(n \leq 10^5, m + k \leq 10^5, d \leq 10\)

Input

第一行为三个整数\(n,m,k\)

第二行为一个\(n\)个数的字符串。

接下来\(m+k\)行每行对应一种操作。

Output

对于每一个\(2\)操作,如果存在,输出一行\(YES\),否则输出\(NO\)

线段树维护哈希

写起来爽,调起来更爽

我们首先预处理出\(po\)数组记录\(base^i\)(这个要用来修改及查询的。)

还要预处理出来\(val[i][j]\)代表长度为\(j\)的全部为数字\(i\)的字符串的哈希值。

然后每次区间合并的时候.
\[
len=tr[rs].r-tr[rs].l+1 \tr[o].va=(tr[ls].va\times po[len]%\ mod +tr[rs].va) %\ mod
\]
这个应该不是很难理解吧。(就类似于你\(hash\)匹配的做法。)

修改时候,我们直接赋值\(tr[o].va=val[k][len]\)即可。

需要注意的有两点:

  1. \(lazy\)标记初值要为\(1\),因为会存在赋值为\(0\)的情况
  2. 查询操作中,当前区间分别在左右两侧的时候\(tr[ls].va \times po[r-mid]\)!!

因此直接码代码就好了

还有一个神仙结论是做题的根据。

如果询问为\((l,r,d)\),则只需要判断\((l+d,r)\)和\((l,r-d)\)即可。

证明的话,我不太会.但是这是正确的。

如果这题卡单\(hash\)的话可以写双\(hash\)。稍作修改即可。不多\(BB\)了.

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#define lo long long
#define base 31
#define mod 20020303
#define R register

using namespace std;

const int gz=1e5+8;

inline void in(R int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int n,m,K,po[gz]={1},val[10][gz];

char s[gz];

struct wc
{
    int l,r,tg;
    lo va;
}tr[gz<<2];

inline void pre()
{
    for(R int i=1;i<gz;i++)
        po[i]=po[i-1]*base%mod;
    for(R int i=0;i<10;i++)
        for(R int j=1;j<gz;j++)
            val[i][j]=(val[i][j-1]*base%mod+i)%mod;
}

#define ls o<<1
#define rs o<<1|1

inline void up(R int o)
{
    tr[o].va=(tr[ls].va*po[tr[rs].r-tr[rs].l+1]%mod+tr[rs].va%mod)%mod;
}

void build(R int o,R int l,R int r)
{
    tr[o].l=l,tr[o].r=r;tr[o].tg=-1;
    if(l==r)
    {
        tr[o].va=s[l]-'0';
        return;
    }
    R int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    up(o);
}

inline void down(R int o)
{
    if(tr[o].tg==-1)return;
    R int k=tr[o].tg;
    tr[ls].va=val[k][tr[ls].r-tr[ls].l+1];
    tr[rs].va=val[k][tr[rs].r-tr[rs].l+1];
    tr[ls].tg=tr[rs].tg=k;
    tr[o].tg=-1;
}

void change(R int o,R int l,R int r,R int k)
{
    if(tr[o].l==l and tr[o].r==r)
    {
        tr[o].tg=k;
        tr[o].va=val[k][tr[o].r-tr[o].l+1];
        return ;
    }
    down(o);
    R int mid=(tr[o].l+tr[o].r)>>1;
    if(r<=mid)change(ls,l,r,k);
    else if(l>mid)change(rs,l,r,k);
    else change(ls,l,mid,k),change(rs,mid+1,r,k);
    up(o);
}

lo query(R int o,R int l,R int r)
{
    if(tr[o].l==l and tr[o].r==r)return tr[o].va;
    down(o);
    R int mid=(tr[o].l+tr[o].r)>>1;
    if(r<=mid)return query(ls,l,r);
    else if(l>mid) return query(rs,l,r);
    else
        return ((query(ls,l,mid)%mod)*po[r-mid]%mod+query(rs,mid+1,r)%mod)%mod;//注意这里!!
}

int main()
{
    pre();
    in(n),in(m),in(K);
    R int tt=m+K;
    scanf("%s",s+1);
    build(1,1,n);
    for(R int opt,l,r,k;tt;tt--)
    {
        in(opt),in(l),in(r),in(k);
        switch(opt)
        {
            case 1:change(1,l,r,k);break;
            case 2:
            {
                if(r-l+1==k)
                {
                    puts("YES");
                    continue;
                }
                puts(query(1,l,r-k)==query(1,l+k,r) ? "YES":"NO");
                break;
            }
        }
    }
}

原文地址:https://www.cnblogs.com/-guz/p/9910979.html

时间: 2024-08-06 14:57:30

线段树+哈希【CF580E】Kefa and Watch的相关文章

线段树 + 字符串Hash - 580E Kefa and Watch

Kefa and Watch Problem's Link Mean: 给你一个长度为n的字符串s,有两种操作: 1 L R C : 把s[l,r]全部变为c; 2 L R d : 询问s[l,r]是否是周期为d的重复串. analyse: n最大为1e5,且m+k最大也为1e5,这就要求操作1和操作2都要采用logn的算法,所以用线段树. 对于更新操作,使用区间更新就可解决. 主要是如何在logn的时间内完成询问操作. 我们采用线段树维护hash值的方法. 结合于类似KMP的性质,我们发现,字

HDU3973 线段树 + 字符哈希

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3973 , 线段树 + 字符哈希,好题. 又学了一种新的哈希方法,hhhh~ 解法: 想法是用P进制的数来表示一个字符串,由于可能数太大,所以就将转换成是十进制后的数模long long的最大值,这样虽然也有可能冲突,但是概率会非常小.这里的P可以随意取一个素数(我取的是31). 先用上面提到的哈希方法将W集合中的字符串都转成十进制数存在数组中,然后进行排序:每一次询问时候,将询问区间的子串转成十进制

HLG 1808 绘画 (哈希 + map + 线段树)

链接: http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=1808 Description 小胖子最近喜欢上了画画,但是他的画画技术很差,只能从零开始,小胖子在一条线上画了很多彩色的球,但是觉得不好看,就想修改. 小胖子站在画前想了又想,他有时会将一些连续的点涂成相同的颜色.但他感觉累的时候,就会无聊的数数某个颜色在某段区间内出现的次数. Input 每组数据的第一行输入两个正整数n和m(1&l

CF213E Two Permutations 线段树维护哈希值

当初竟然看成子串了$qwq$,不过老师的$ppt$也错了$qwq$ 由于子序列一定是的排列,所以考虑插入$1$到$m$到$n-m+1$到$n$; 如何判断呢?可以用哈希$qwq$: 我们用线段树维护哈希值,合并时用就把左子树的哈希值$x[ls]$在$B$进制下左移$sum[rs]$位,即$x[tr]=x[ls]*p[sum[rs]]+x[rs]$; 此时就可以向上更新哈希值. #include<cstdio> #include<iostream> #include<algor

【线段树+HASH】CODEFORCES 580E Kefa and Watch

通道 题意:0-9字符串,区间修改,区间询问是否d周期 思路:直接暴力线段树,然后HASH修改和查询,卡HASH的话就双HASH. 代码: #include<cstdio> #include<cstring> typedef long long ll; const int N = 100007; int n, m, k, lens; char s[N]; #define lch id<<1 #define rch id<<1|1 class HashTree

POJ 2777 Count Color (线段树区间更新加查询)

Description Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem. There is a very long board with length L centimeter, L is a positive integer, so we can evenly d

BZOJ 2124等差子序列 线段树&amp;&amp;hash

[题目描述 Description] 给一个 1 到 N 的排列{Ai},询问是否存在 1<=p1<p2<p3<p4<p5<…<pLen<=N(Len>=3),使得 Ap1,Ap2,Ap3,…ApLen 是一个等差序列. [输入描述 Input Description] 输入的第一行包含一个整数 T,表示组数. 下接 T 组数据,每组第一行一个整数 N,每组第二行为一个 1 到 N 的排列, 数字两两之间用空格隔开. [输出描述 Output Desc

F - Problem F( 线段树 )

度熊手上有一本字典存储了大量的单词,有一次,他把所有单词组成了一个很长很长的字符串.现在麻烦来了,他忘记了原来的字符串都是什么,神奇的是他竟然记得原来那些字符串的哈希值.一个字符串的哈希值,由以下公式计算得到: H(s)=∏i≤len(s)i=1(Si?28) (mod 9973)SiSi代表 S[i] 字符的 ASCII 码. 请帮助度熊计算大字符串中任意一段的哈希值是多少. Input多组测试数据,每组测试数据第一行是一个正整数NN,代表询问的次数,第二行一个字符串,代表题目中的大字符串,接

[bzoj3207]花神的嘲讽计划Ⅰ[可持久化线段树,hash]

将每k个数字求一个哈希值,存入可持久化线段树,直接查询即可 1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cmath> 7 #include <ctime> 8 9 using namespace std; 10 11 #define