数论--阶乘幂&扩展欧拉定理--牛客练习赛22E 简单数据结构1

https://www.nowcoder.com/acm/contest/132/E

给定数组A,有2种操作

1.区间更新,a[l]...a[r]都加x

2.求阶乘幂 a[l] ^ (a[l+1] ^ (...a[r-1] ^ a[r]))

解:

区间更新用树状数组

A[i]记录原数组

delta[i]记录a[i]到a[n]共同的增量

xdelta[i] = i * delta[i]

sum[i] = (A[1] + ... + A[i]) + (i + 1) * (delta[1] + ... + delta[i]) - (xdelta[1] + ... + xdelta[i])

阶乘幂用扩展欧拉定理

ps:

ll modp(ll a,ll p){return a >= p ? a % p + p : a;}
ll qk_pow(ll a,ll b,ll p)
{
    ll ans = 1;
    a = modp(a,p);
    while(b){
        if(b & 1) ans = modp(ans * a,p);
        b >>= 1;
        a = modp(a * a,p);
    }
    return ans;
}

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 5e5 + 5;
const int maxp = 2e7 + 5;
int phi[maxp];

typedef long long ll;

ll A[maxn];
ll delta[maxn];
ll xdelta[maxn];

int n,m;
void init()
{
    for(int i = 2;i < maxp;i ++){
        if(phi[i] == 0) {
            phi[i] = i - 1;
            for(int j = i + i;j < maxp;j += i){
                if(phi[j] == 0) phi[j] = j;
                phi[j] = phi[j] / i * (i - 1);
            }
            }
    }
}
void update(ll arr[],int x,ll k)
{
    while(x < maxn){
        arr[x] += k;
        x += (x & -x);
    }
}
ll sum(ll arr[],int x)
{
    ll ans = 0;
    while(x){
        ans += arr[x];
        x -= (x & -x);
    }
    return ans;
}
ll getA(int x)
{
    ll ans1 = sum(A,x) + (ll)(x + 1) * sum(delta,x) - sum(xdelta,x);
    ll ans2 = 0;
    x -- ;
    if(x) ans2 = sum(A,x) + (ll)(x + 1) * sum(delta,x) - sum(xdelta,x);
    return ans1 - ans2;
}

ll modp(ll a,ll p){return a >= p ? a % p + p : a;}
ll qk_pow(ll a,ll b,ll p)
{
    ll ans = 1;
    a = modp(a,p);
    while(b){
        if(b & 1) ans = modp(ans * a,p);
        b >>= 1;
        a = modp(a * a,p);
    }
    return ans;
}
ll f(int ql,int qr,int p)
{
    if(p == 1) return 1;
    ll a = getA(ql);
    if(ql == qr ) return modp(a,p);
    ll x = f(ql + 1,qr,phi[p]);
    ll ans = qk_pow(a,x,p);
    return ans;
}
int main()
{
    init();
    while(scanf("%d%d",&n,&m) != EOF)
    {
        memset(A,0,sizeof(A));
        memset(delta,0,sizeof(delta));
        memset(xdelta,0,sizeof(xdelta));
        
        int t;
        for(int i = 1;i <= n;i ++){
            scanf("%d",&t);
            update(A,i,t);
        }
        int op;
        ll l,r,p;
        while(m --){
            scanf("%d%lld%lld%lld",&op,&l,&r,&p);
            if(op == 1){
                update(delta,l,p);
                update(delta,r + 1,-p);
                update(xdelta,l,l * p);
                update(xdelta,r + 1,-p * (r + 1));
            }
            else{
                ll ans = f(l,r,p);
                ans = (ans % p + p) % p;
                printf("%lld\n",ans);
            }
        }
        
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值