BZOJ4869 || 洛谷P3747 [SHOI2017]相逢是问候【欧拉定理+线段树】

Time Limit: 40 Sec
Memory Limit: 512 MB

Description

Informatikverbindetdichundmich.
信息将你我连结。B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数。一共有m个操作,可以
分为两种:0 l r表示将第l个到第r个数(al,al+1,…,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是
输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为
这个结果可能会很大,所以你只需要输出结果mod p的值即可。

Input

第一行有三个整数n,m,p,c,所有整数含义见问题描述。
接下来一行n个整数,表示a数组的初始值。
接下来m行,每行三个整数,其中第一个整数表示了操作的类型。
如果是0的话,表示这是一个修改操作,操作的参数为l,r。
如果是1的话,表示这是一个询问操作,操作的参数为l,r。
1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p

Output

对于每个询问操作,输出一行,包括一个整数表示答案mod p的值。


题目分析

如果做过这两题(特别是前一道)的话再看这题会容易很多
BZOJ3884 上帝与集合的正确用法
洛谷P4145 上帝造题的七分钟2

首先这题最关键的突破口在于

  • 对于同一个位置的数,执行一定次数的修改操作后这个数就不会再变化,而这个次数是对数级

每次修改后产生的数可以看作 c c c . . . a c^{c^{c^{...^{a}}}} ccc...a,这与BZOJ3884是一样的形式
可以根据扩展欧拉定理 a , p ∈ Z a,p\in Z a,pZ,则 a b = a^b= ab= { a b , b &lt; φ ( p ) a b m o d &ThinSpace;&ThinSpace; φ ( p ) + φ ( p ) , b &gt; = φ ( p ) \left\{\begin{aligned}a^b,b&lt;\varphi(p)\\ a^{b\mod\varphi(p)+\varphi(p) },b&gt;=\varphi(p)\end{aligned}\right. {ab,b<φ(p)abmodφ(p)+φ(p),b>=φ(p) m o d &ThinSpace;&ThinSpace; p \mod p modp递归处理
递归的边界是模数 p = = 1 p==1 p==1

也就是说一个数需要处理的修改次数最多只有 p p p开始不断进行 p = φ ( p ) p=\varphi(p) p=φ(p)直到 p = = 1 p==1 p==1的次数
根据欧拉phi函数计算公式 φ ( N ) = N ∗ ∏ i = 1 k p i − 1 p i \varphi(N)=N*\prod_{i=1}^k\frac{p_i-1}{p_i} φ(N)=Ni=1kpipi1
N N N为偶数,它一定存在质因子 2 2 2,计算式中一定存在 × 1 2 \times\frac{1}{2} ×21
N N N为奇数,因为质数(除2以外)一定是奇数,则 p i − 1 p_i-1 pi1一定是偶数,所以 φ ( N ) \varphi(N) φ(N)一定是偶数
所以上述的迭代次数一定是对数级

我们记上述最大修改次数为 c n t cnt cnt(可以预处理)
既然每个数被修改次数是对数级的,那么可以仿照 洛谷P4145 上帝造题的七分钟2]
用线段树维护区间和、区间最小修改次数 m i mi mi
每次修改直接进入到最低层修改,若发现某个区间 m i &gt; = c n t mi&gt;=cnt mi>=cnt,则可以不进入该区间修改

这样每次修改递归 l o g p logp logp层,每层快速幂计算 O ( l o g p ) O(logp) O(logp),每个数最多被修改 l o g p logp logp
复杂度 O ( n l o g n ∗ l o g 3 p ) O(nlogn*log^3p) O(nlognlog3p) 只能信仰过吧

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lt;

lt read()
{
    lt f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=50010;
lt n,m,mod,c;
lt a[maxn],sum[maxn<<2],mi[maxn<<2];
lt phi[maxn],cnt,rem;

lt qpow(lt aa,lt k,lt mm)
{
    lt res=1; rem=0;
    while(k>0){
        if(k&1) res=res*aa;
        aa=(aa*aa)%mm; k>>=1;
        if(res>=mm) rem=1,res%=mm;
    }
    return res;
}

void build(int s,int t,int p)
{
    if(s==t){ sum[p]=a[s]%mod; return;}
    int mid=s+t>>1;
    build(s,mid,p<<1); build(mid+1,t,p<<1|1);
    sum[p]=(sum[p<<1]+sum[p<<1|1])%mod;
}

lt Phi(lt x)
{
    lt res=x;
    for(int i=2;i*i<=x;++i)
    if(x%i==0)
    {
        res=res/i*(i-1);
        while(x%i==0) x/=i;
    }
    if(x>1) return res/x*(x-1);
    else return res;
} 

void work(lt x)
{
    phi[0]=x;
    while(x!=1) 
    {
        x=Phi(x);
        phi[++cnt]=x;
    }
    phi[++cnt]=1;
}

lt qsum(int ll,int rr,int s,int t,int p)
{
    if(ll<=s&&t<=rr) return sum[p]%mod;
    int mid=s+t>>1; lt ans=0;
    if(ll<=mid) ans=(ans+qsum(ll,rr,s,mid,p<<1))%mod;
    if(rr>mid) ans=(ans+qsum(ll,rr,mid+1,t,p<<1|1))%mod;
    return ans;
}

lt calc(lt val,int lev,int k)
{
    if(lev==0){
    	if(val>=phi[k]) rem=1;
    	return val;
    }
    lt tt=calc(val,lev-1,k+1);
    tt=rem?tt%phi[k+1]+phi[k+1]:tt;
    return qpow(c,tt,phi[k]);
}

void update(int ll,int rr,int s,int t,int p)
{
    if(mi[p]>=cnt) return;
    if(s==t){ 
    	++mi[p];
        sum[p]=calc(a[s],mi[p],0);
        return;
    }
    int mid=s+t>>1;
    if(ll<=mid&&mi[p<<1]<cnt) update(ll,rr,s,mid,p<<1);
    if(rr>mid&&mi[p<<1|1]<cnt) update(ll,rr,mid+1,t,p<<1|1);
    sum[p]=(sum[p<<1]+sum[p<<1|1])%mod;
    mi[p]=min(mi[p<<1],mi[p<<1|1]);
}

int main()
{
    n=read();m=read();mod=read();c=read();
    for(int i=1;i<=n;++i) a[i]=read();
    build(1,n,1);

    work(mod);
    while(m--)
    {
        int opt=read(),ll=read(),rr=read();
        if(opt==0) update(ll,rr,1,n,1);
        else if(opt==1) printf("%lld\n",qsum(ll,rr,1,n,1));
    }
    return 0;
}

由于三个log的复杂度不是很可观,所以考虑预处理c的幂次方
直接开数组存不下,可以用 p o w 1 [ i ] pow1[i] pow1[i]表示 c i c^i ci p o w 2 [ i ] pow2[i] pow2[i]表示 c i ∗ 10000 c^{i*10000} ci10000
c k = p o w 1 [ k m o d &ThinSpace;&ThinSpace; 10000 ] ∗ p o w [ k / 10000 ] c^k=pow1[k\mod10000]*pow[k/10000] ck=pow1[kmod10000]pow[k/10000]
预处理的时候顺便记录下对应扩展欧拉定理哪一条
总复杂度 O ( n l o g n ∗ l o g 2 p ) O(nlogn*log^2p) O(nlognlog2p)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lt;
   
lt read()
{
    lt f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}
   
const int maxn=50010;
lt n,m,mod,c;
lt a[maxn],sum[maxn<<2],mi[maxn<<2];
lt phi[maxn],cnt,judge;
lt tpow[maxn][110][2],rem[maxn][110][2];
   
void build(int s,int t,int p)
{
    if(s==t){ sum[p]=a[s]%mod; return;}
    int mid=s+t>>1;
    build(s,mid,p<<1); build(mid+1,t,p<<1|1);
    sum[p]=(sum[p<<1]+sum[p<<1|1])%mod;
}
   
lt Phi(lt x)
{
    lt res=x;
    for(int i=2;i*i<=x;++i)
    if(x%i==0)
    {
        res=res/i*(i-1);
        while(x%i==0) x/=i;
    }
    if(x>1) return res/x*(x-1);
    else return res;
} 
   
void work(lt x)
{
    phi[0]=x;
    while(x!=1) 
    {
        x=Phi(x);
        phi[++cnt]=x;
    }
    phi[++cnt]=1;
      
    for(int i=0;i<=cnt;++i)
    {
        tpow[0][i][0]=1;
        for(int j=1;j<=10000;++j)
        {
            tpow[j][i][0]=tpow[j-1][i][0]*c;
            if(tpow[j][i][0]>=phi[i]) tpow[j][i][0]%=phi[i],rem[j][i][0]=1;
            rem[j][i][0]|=rem[j-1][i][0];
        }
    }
    for(int i=0;i<=cnt;++i)
    {
        tpow[0][i][1]=1;
        rem[1][i][1]=rem[10000][i][0];
        for(int j=1;j<=10000;++j)
        {
            tpow[j][i][1]=tpow[j-1][i][1]*tpow[10000][i][0];
            if(tpow[j][i][1]>=phi[i]) tpow[j][i][1]%=phi[i],rem[j][i][1]=1;
            rem[j][i][1]|=rem[j-1][i][1];
        }
    }
}
   
lt qsum(int ll,int rr,int s,int t,int p)
{
    if(ll<=s&&t<=rr) return sum[p]%mod;
    int mid=s+t>>1; lt ans=0;
    if(ll<=mid) ans=(ans+qsum(ll,rr,s,mid,p<<1))%mod;
    if(rr>mid) ans=(ans+qsum(ll,rr,mid+1,t,p<<1|1))%mod;
    return ans;
}
   
lt qpow(lt k,lt id)
{
    judge=0;
    lt res=tpow[k%10000][id][0]*tpow[k/10000][id][1];
    if(res>=phi[id]) res=res%phi[id],judge=1;
    judge|=rem[k%10000][id][0]|rem[k/10000][id][1];
    return res;
}
  
lt calc(lt val,int lev,int k)
{
    judge=0;
    if(lev==0){
        if(val>=phi[k]) judge=1,val%=phi[k];
        return val;
    }
    lt tt=calc(val,lev-1,k+1);
    if(judge) tt=tt%phi[k+1]+phi[k+1];
    return qpow(tt,k);
}
   
void update(int ll,int rr,int s,int t,int p)
{
    if(mi[p]>=cnt) return;
    if(s==t){ 
        ++mi[p];
        sum[p]=calc(a[s],mi[p],0);
        return;
    }
    int mid=s+t>>1;
    if(ll<=mid&&mi[p<<1]<cnt) update(ll,rr,s,mid,p<<1);
    if(rr>mid&&mi[p<<1|1]<cnt) update(ll,rr,mid+1,t,p<<1|1);
    sum[p]=(sum[p<<1]+sum[p<<1|1])%mod;
    mi[p]=min(mi[p<<1],mi[p<<1|1]);
}
   
int main()
{
    n=read();m=read();mod=read();c=read();
    for(int i=1;i<=n;++i) a[i]=read();
    build(1,n,1);
   
    work(mod);
    while(m--)
    {
        int opt=read(),ll=read(),rr=read();
        if(opt==0) update(ll,rr,1,n,1);
        else if(opt==1) printf("%lld\n",qsum(ll,rr,1,n,1));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值