[洛谷P3934]Nephren Ruq Insania/[BZOJ5394][Ynoi2016]炸脖龙:扩展欧拉定理+线段树

我永远喜欢奈芙莲.jpg

分析:

数论数学和数据结构完美的融合。
通过支持区间加的线段树维护每个位置的数,可以通过标记永久化显著减小常数。
使用扩展欧拉定理降幂,这样对于每个操作只需进行log次降幂即可将模数降为1。
注意边界处理。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN=500005;
inline LL read(){
    LL x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,prm[20000005>>3],phi[20000005],cnt,loc,ql,qr;
LL a[MAXN],sum[MAXN<<2],add[MAXN<<2],k;
bool vis[20000005];
void pre_process(){
    n=20000000;
    phi[1]=1;
    for(register int i=2;i<=n;i++){
        if(!vis[i]){
            prm[++cnt]=i;
            phi[i]=i-1;
        }
        for(register int j=1;j<=cnt&&i*prm[j]<=n;j++){
            vis[i*prm[j]]=1;
            if(i%prm[j]==0){
                phi[i*prm[j]]=phi[i]*prm[j];
                break;
            }
            phi[i*prm[j]]=phi[i]*(prm[j]-1);
        }
    }
}
#define mid ((l+r)>>1)
#define lc (o<<1)
#define rc ((o<<1)|1)
void build(int o,int l,int r){
    if(l==r){
        sum[o]=a[l];
        return;
    }
    build(lc,l,mid);
    build(rc,mid+1,r);
}
void upd(int o,int l,int r){
    if(l==r) sum[o]+=k;
    if(ql<=l&&r<=qr){
        add[o]+=k;
        return;
    }
    if(mid>=ql) upd(lc,l,mid);
    if(mid<qr) upd(rc,mid+1,r);
}
LL query(int o,int l,int r){
    if(l==r&&l==loc) return sum[o];
    if(loc<=mid) return query(lc,l,mid)+add[o];
    else return query(rc,mid+1,r)+add[o];
}
#undef mid
#undef lc
#undef rc
LL Qpow(LL x,LL y,LL p){
    LL ans=1,t=x;bool flag=0,tflag=0;
    if(t>=p) tflag=1,t%=p;
    while(y){
        if(y&1){
            ans*=t;
            if(tflag) flag=1;
            if(ans>=p) flag=1,ans%=p;
        }
        t*=t;
        if(t>=p) tflag=1,t%=p;
        y>>=1;
    }
    return flag==1?-ans:ans;
}//返回负数表示取模过 
LL solve(int l,LL p){
    if(p==1) return 0;
    loc=l;LL cur=query(1,1,n);
    if(l==qr) return cur>=p?-cur%p:cur;
    LL tim=solve(l+1,phi[p]);
    if(tim<=0) tim=-tim+phi[p];//返回非正数表示tim需要+phi[p] 
    return Qpow(cur,tim,p)%p;//负数取模还是负数 
}
int main(){
    pre_process();
    n=read(),m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);
    while(m--){
        int opt=read();
        if(opt==1){
            ql=read(),qr=read(),k=read();
            upd(1,1,n);
        }
        else{
            ql=read(),qr=read(),k=read();
            printf("%lld\n",abs(solve(ql,k)%k));//同 
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/9502322.html