zoukankan      html  css  js  c++  java
  • 题解 CF580E 【Kefa and Watch】

    线段树+Hash

    听说这方法比不过暴力?(雾)

    解题过程

    首先考虑第二个问题,如何快速求解一个子串是不是另一个串的循环节。

    此处有一个结论:
    假设 S 为 的长度为 n 。
    只要判断 S[1…n-d+1] 和 S[d+1…n] 是否相等即可。
    简单的想,假设当前循环节是(k),那么就有:
    (a)
    (a*ch+b)
    (a*ch^2+b*ch+c)
    (a*ch^3+b*ch^2+c*ch+d)
    (a*ch^4+b*ch^3+c*ch^2+d*ch+e)
    (a*ch^5+b*ch^4+c*ch^3+d*ch^2+e*ch+f)
    必从(k)个字母循环,假设(k=2).那么就有:
    (a)
    (a*ch+b)
    (a*ch^2+b*ch+a)
    (a*ch^3+b*ch^2+a*ch+b)
    (a*ch^4+b*ch^3+a*ch^2+b*ch+a)
    (a*ch^5+b*ch^4+a*ch^3+b*ch^2+a*ch+b)
    发现前面两个和后面两个Hash值相等,再通过数学归纳法,可以证明。
    其实也很好想,因为我们要通过Hash[n]-Hash[n-d]相减来判断是不是s[0]-s[d]的多少(ch)倍,必须满足后面几个相等,因为只有是循环节,才能将前面的抵消掉。

    第一个问题,如何处理赋值与Hash值之间的关系

    一段的一段的赋值,查找也是一段一段的,很容易可以想到用线段树来维护一个Hash值。
    每个点存当前的Hash值,但是这里就有一个问题,因为是存的从(ch^0)开始的,那么怎么才能转移到父亲节点呢?

    kano[x].h=((kano[x<<1].h*Pow[kano[x<<1|1].rkano[x<<1|1].l+1]+kano[x<<1|1].h)%mod+mod)%mod;
    
    

    左儿子里的值和他在父亲节点应当是的值差了什么呢?
    根据Hash函数的定义:(Hash(l,r)=Hash[r]-Hash[l-1]*p^{r-l+1})
    所以说,左儿子里的值就差了乘上一个(ch^{sizeof(rightson)})
    这个问题解决了,那(pushdown)又该如何呢?

    void pushdown(int x){
    	if(kano[x].lazy+1){
    		kano[x<<1].lazy=kano[x<<1|1].lazy=kano[x].lazy;
    		kano[x<<1].h=Pow2[kano[x<<1].r-kano[x<<1].l]*kano[x].lazy%mod;
    		kano[x<<1|1].h=Pow2[kano[x<<1|1].r-kano[x<<1|1].l]*kano[x].lazy%mod;
    		kano[x].lazy=-1;
    	}
    	return ;
    }
    

    (lazy)里存的是将要变成的值,那么如何将这些值赋到儿子中呢?
    可以看,在没有赋值前假设左儿子的值是:
    (x*ch^3+y*ch^2+z*ch)
    那么赋值之后就应该是:(k*ch^3+k*ch^2+k*ch).
    可以发现,就是在(k)的基础上(*)了一个(ch^3+ch^2+ch),因此就可以同时维护一个(ch^x+xh^{x-1}+ch^{x-2}+……ch)的一个东西,就可以快速地(pushdown)

    注意事项

    1.有递归操作时,一定都下传(lazy)
    2.注意边界。
    3.我每次修改时都将k加了1,防止0,(lazy)为-1,防止0的出现。

    参考代码

    #include<bits/stdc++.h>
    using namespace std;
    const int b=17;
    const int N=1e5+50;
    const int mod=1e9+9;
    typedef unsigned long long ULL;
    char s[N];
    int n,m,opt,k,l,r;
    ULL Pow[N],Pow2[N];
    struct Kano{
    	int l,r,lazy;
    	ULL h;
    }kano[N*6];
    void pushup(int x){
    	kano[x].h=((kano[x<<1].h*Pow[kano[x<<1|1].r-kano[x<<1|1].l+1]+kano[x<<1|1].h)%mod+mod)%mod;
    }
    void pushdown(int x){
    	if(kano[x].lazy+1){
    		kano[x<<1].lazy=kano[x<<1|1].lazy=kano[x].lazy;
    		kano[x<<1].h=Pow2[kano[x<<1].r-kano[x<<1].l]*kano[x].lazy%mod;
    		kano[x<<1|1].h=Pow2[kano[x<<1|1].r-kano[x<<1|1].l]*kano[x].lazy%mod;
    		kano[x].lazy=-1;
    	}
    	return ;
    }
    void build(int x,int l,int r){
    	kano[x].l=l;kano[x].r=r;kano[x].lazy=-1;
    	if(l==r){
    		kano[x].h=s[l]-'0'+1;
    		return;
    	}
    	int mid=(kano[x].l+kano[x].r)>>1;
    	build(x<<1,l,mid),build(x<<1|1,mid+1,r);
    	pushup(x);
    }
    void add(int x,int l,int r,int a){
    	if(kano[x].l==l&&kano[x].r==r){
    		kano[x].lazy=a;
    		kano[x].h=Pow2[kano[x].r-kano[x].l]*a%mod;
    		return;
    	}
    	pushdown(x);
    	int mid=(kano[x].l+kano[x].r)>>1;
    	if(mid>=r) add(x<<1,l,r,a);
    	else if(mid<l) add(x<<1|1,l,r,a);
    	else add(x<<1,l,mid,a),add(x<<1|1,mid+1,r,a);
    	pushup(x);
    }
    ULL query(int x,int l,int r){
    	if(l>r) return 0;
    	if(kano[x].l==l&&kano[x].r==r)
    		return kano[x].h;
    	pushdown(x);
    	int mid=(kano[x].l+kano[x].r)>>1;
    	if(mid>=r) return query(x<<1,l,r);
    	else if(mid<l) return query(x<<1|1,l,r);
    	else return (query(x<<1,l,mid)*Pow[r-mid]+query(x<<1|1,mid+1,r))%mod;
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&k);
    	m+=k;
    	scanf("%s",s+1);
    	Pow[0]=Pow2[0]=1;
    	for(int i=1;i<=n;i++) Pow[i]=Pow[i-1]*b%mod,Pow2[i]=(Pow2[i-1]*b+1)%mod;
    	build(1,1,n);
    	for(int i=1;i<=m;i++){
    		scanf("%d%d%d%d",&opt,&l,&r,&k);
    		if(opt==1) add(1,l,r,k+1);
    		else printf("%s
    ",query(1,l,r-k)==query(1,l+k,r)?"YES":"NO");
    	}
    }
    

    听说这东西打起来很爽,调起来更爽.(雾)

  • 相关阅读:
    Akka框架使用注意点
    log4j配置文件加载
    iptables常规使用
    linux ipv6临时地址
    组合数取模Lucas定理及快速幂取模
    Shell变量的定义与赋值操作注意事项
    虚拟机软件bochs编译使用问题
    实现一个简陋操作系统的相关笔记日志
    linux内核增加系统调用--Beginner's guide
    c语言几种异常
  • 原文地址:https://www.cnblogs.com/Fast-Bird/p/11974851.html
Copyright © 2011-2022 走看看