毒瘤数据结构之奈芙莲树

Posted yzhang-rp-inf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了毒瘤数据结构之奈芙莲树相关的知识,希望对你有一定的参考价值。

〇、奈芙莲美图欣赏

技术分享图片

技术分享图片

技术分享图片

奈芙莲也是很可爱呢

~~一、什么是奈芙莲树

是不是大佬蒟蒻yzhang又发明了珂学算法(逃~)

奈芙莲树,听起来好dl

实际呢只是题目和奈芙莲有关系,所以起了这个珂学的名字(逃~

是一种奇怪的数据结构瞎搞

是由线段树改造而成(平易近人)

二、奈芙莲树资瓷什么操作

资瓷一些奇怪的操作(大雾)

1.维护:区间加减(奈芙莲树好型很简单)

(实际很简单)

2.查询:有区间【l,r】,求(a[l]^a[l+1]^a[l+2]^...^a[r-1]^a[r])%p

a^b表示a的b次方

(好毒瘤啊)

三、拓展欧拉定理

什么??!我们不是要学奈芙莲树吗?拓展欧拉是什么鬼,我数论很差啊!!!

题目让你求a[l]^a[l+1]^a[l+2]^...^a[r-1]^a[r]

怎么求??!

暴力? 快速幂? 高精? Python?

要用拓展欧拉定理(毒瘤)

什么是拓展欧拉定理(逃~?

我们先说欧拉定理

以下所有“==”表示同余

a^phi[p]==1(mod p) gcd(a,p)=1

phi是什么?

表示小于p的正整数中与p互质的数的个数 gcd不用说 (证明略)

我们再说拓展欧拉定理

这是核心

  1. gcd(a,b)=1 a^b==a^(b%phi[p]) (mod p)

  2. gcd(a,b)!=1&&b<phi[p] 直接快速幂

  3. gcd(a,b)!=1&&b>=phi[p] a^b==a^(b%phi[p]+phi[p]) (mod p)

(证明略,不懂得赶紧问度娘)

关于扩展欧拉定理的题

No1. Luogu P4139 上帝与集合的正确用法

链接:https://www.luogu.org/problemnew/show/P4139

No2. Luogu P3747 [六省联考2017]相逢是问候

链接:https://www.luogu.org/problemnew/show/P3747

四、奈芙莲树有哪些题

No1. Luogu P3934 Nephren Ruq Insania

简介:板子题,名称的来源

链接:https://www.luogu.org/problemnew/show/P3934

No.2 Luogu P4118 [Ynoi2016]炸脖龙I

简介:板子题,Luogu不能交,也许bzoj有

题面链接:https://www.luogu.org/problemnew/show/P4118

五、奈芙莲树的初始化

没什么,预处理质数和欧拉函数。

inline void calcprime()
{
    phi[1]=1;    //边界条件
    for(register int i=2;i<=lim;++i)    //日常卡常
    {
        if(!vis[i])    
        {
            prime[++cnt]=i;    //筛法
            phi[i]=i-1;    //质数只有自己不与它互质
        }
        for(register int j=1;j<=cnt;++j)
        {
            int t=i*prime[j];    //筛法
            if(t>lim)    //超过最大(题目数据范围)
                break;
            vis[t]=true;    //表明不是质数
            if(i%prime[j]==0)    //特判欧拉函数
            {
                phi[t]=phi[i]*prime[j];
                break;
            }
            phi[t]=phi[i]*(prime[j]-1);
        }
    }
}

预处理就这样简单(逃~

五、奈芙莲树的查询操作

基于普通的线段树

再用拓展欧拉公式一求就行

inline ll querysum(int x,int l,int r,int q)    //真正的查询
{
    if(l==r)    //特判
        return sum[x];
    int mid=(l+r)>>1;
    pushdown(x,l,r);    //把lazy_tag下放
    if(q<=mid)
        return querysum(x<<1,l,mid,q);
    else
        return querysum(x<<1|1,mid+1,r,q);
}
inline ll query(int q)    //查询
{
    if(las[q]==m)    //是否有记录
        return a[q];
    las[q]=m;    //计入记录
    return a[q]=querysum(1,1,n,q);    //计入记录+返回
}
inline ll fpow(int x,ll p,int q)    //快速幂
{
    int ans=1;
    for(;p;p>>=1,x=1LL*x*x%q)
        if(p&1)
            ans=1LL*ans*x%q;
    return ans;
}
inline ll calc(int l,int r,int p)
{
    if(query(l)%p==0)    //一堆特判
        return 0;
    if(p==1)
        return 1;
    if(l==r)
    {
        ll curt=query(l);
        return curt%p+(curt>=p)*p;    //公式
    }
    //一堆瞎搞
    int f=min(r,l+5);
    for(register int i=l+1;i<=f;++i)
        if(query(i)==1)
        {
            f=i;
            break;
        }
    ll last=query(f),q=0;
    for(register int i=f-1;i>=l+1;--i)
    {
        q=last,last=1;
        while(q--)
        {
            last*=query(i);
            if(last>=phi[p])
                return fpow(query(l)%p,calc(l+1,r,phi[p])+phi[p],p);
        }
    }
    return fpow(query(l)%p,last,p);
}

实际很简单(大雾)

六、奈芙莲树的完整程序

#pragma GCC optimize("-O3")
#include <bits/stdc++.h>
#define Chtholly_is_so_cute ios::sync_with_stdio(0);
#define ll long long
using namespace std;
const int N=500005;
const int M=20000005;
const int inf=N-4;
const int lim=20000000;
int n,m,x,phi[M],prime[M],cnt;
ll c[N],a[N];
bool vis[M];
int las[N];
ll sum[N<<2],tag[N<<2],fone[N<<2];
inline int read()
{
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch==‘-‘)f=-1;}while(ch<‘0‘||ch>‘9‘);
    do{x=x*10+ch-‘0‘;ch=getchar();}while(ch>=‘0‘&&ch<=‘9‘);
    return f*x;
}
inline void calcprime()
{
    phi[1]=1;
    for(register int i=2;i<=lim;++i)
    {
        if(!vis[i])
        {
            prime[++cnt]=i;
            phi[i]=i-1;
        }
        for(register int j=1;j<=cnt;++j)
        {
            int t=i*prime[j];
            if(t>lim)
                break;
            vis[t]=true;
            if(i%prime[j]==0)
            {
                phi[t]=phi[i]*prime[j];
                break;
            }
            phi[t]=phi[i]*(prime[j]-1);
        }
    }
}
inline void pushup(int x)
{
    sum[x]=sum[x<<1]+sum[x<<1|1];
    fone[x]=min(fone[x<<1],fone[x<<1|1]);
}
inline void build(int x,int l,int r)
{
    fone[x]=inf;
    if(l==r)
    {
        sum[x]=a[l];
        if(a[l]==1)
            fone[x]=l;
        return;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    pushup(x);
}
inline void pushdown(int x,int l,int r)
{
    if(tag[x])
    {
        int mid=(l+r)>>1;
        int ls=x<<1;
        int rs=ls+1;
        tag[ls]+=tag[x];
        tag[rs]+=tag[x];
        sum[ls]+=tag[x]*1LL*(mid-l+1);
        sum[rs]+=tag[x]*1LL*(r-mid);
        fone[ls]=inf;
        fone[rs]=inf;
        tag[x]=0;
    }
}
inline void update(int x,int l,int r,int L,int R,int v)
{
    if(L<=l&&r<=R)
    {
        sum[x]+=1LL*(r-l+1)*v;
        tag[x]+=v;
        fone[x]=inf;
        return;
    }
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    if(L<=mid)
        update(x<<1,l,mid,L,R,v);
    if(R>mid)
        update(x<<1|1,mid+1,r,L,R,v);
    pushup(x);
}
inline ll querysum(int x,int l,int r,int q)
{
    if(l==r)
        return sum[x];
    int mid=(l+r)>>1;
    pushdown(x,l,r);
    if(q<=mid)
        return querysum(x<<1,l,mid,q);
    else
        return querysum(x<<1|1,mid+1,r,q);
}
inline ll query(int q)
{
    if(las[q]==m)
        return a[q];
    las[q]=m;
    return a[q]=querysum(1,1,n,q);
}
inline ll fpow(int x,ll p,int q)
{
    int ans=1;
    for(;p;p>>=1,x=1LL*x*x%q)
        if(p&1)
            ans=1LL*ans*x%q;
    return ans;
}
inline ll calc(int l,int r,int p)
{
    if(query(l)%p==0)
        return 0;
    if(p==1)
        return 1;
    if(l==r)
    {
        ll curt=query(l);
        return curt%p+(curt>=p)*p;
    }
    int f=min(r,l+5);
    for(register int i=l+1;i<=f;++i)
        if(query(i)==1)
        {
            f=i;
            break;
        }
    ll last=query(f),q=0;
    for(register int i=f-1;i>=l+1;--i)
    {
        q=last,last=1;
        while(q--)
        {
            last*=query(i);
            if(last>=phi[p])
                return fpow(query(l)%p,calc(l+1,r,phi[p])+phi[p],p);
        }
    }
    return fpow(query(l)%p,last,p);
}
int main()
{
    Chtholly_is_so_cute
    calcprime();
    n=read(),m=read();
    for(register int i=1;i<=n;++i)
        las[i]=-1;
    for(register int i=1;i<=n;++i)
        a[i]=read();
    build(1,1,n);
    while(m--)
    {
        int opt=read(),l=read(),r=read(),v=read();
        if(opt==1)
            update(1,1,n,l,r,v);
        else
            cout<<(calc(l,r,v)%v)<<endl;
    }
    return 0;
}

以上是关于毒瘤数据结构之奈芙莲树的主要内容,如果未能解决你的问题,请参考以下文章

数据结构平衡树treap

毒瘤数据结构之珂朵莉树

1004F.Sonya and Bitwise OR(毒瘤线段树+分治思想)

BZOJ 4085 丧心病狂的毒瘤题目

UOJ #67. 新年的毒瘤

#10 //I [HNOI/AHOI2018]毒瘤