bzoj 5394: [Ynoi2016]炸脖龙 数论+树状数组

15 篇文章 0 订阅
12 篇文章 0 订阅

给一个长为n的序列,m次操作,每次操作:
这里写图片描述
Input
第一行两个整数 n,m 表示序列长度和操作数
接下来一行,n个整数,表示这个序列
接下来m行,可能是以下两种操作之一:
1 l r x 表示区间[l,r]加上 x
2 l r p 表示对区间[l,r]进行一次查询,模数为 p
n , m <= 500000 , 序列中每个数在 [2,1e9] 内,p <= 2e7, 每次加上的数在 [0,2e9]
共10组数据
Output
对于每个询问,输出一个数表示答案。

利用欧拉定理
这里写图片描述
这里不用求gcd,只需考虑gcd不为1的情况,更通用
对一个数x取phi[x],最多几次就取到1,所以可以暴力
区间修改用树状数组维护,每次暴力的时候查询一下当前值。
注意特判 res 和 当前 p 的大小关系

///#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define maxn 500020
#define N 20000000
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e8
#define lowbit(x) (x&(-x))


typedef long long ll;
inline int read(){
    register int num = 0;
    register char ch = getchar();
    while ( ch > '9' || ch < '0' ) ch = getchar();
    while ( ch <= '9' && ch >= '0' ) num = num * 10 + ch - '0' , ch = getchar();
    return num;
}
bool tag[N + 20];
int phi[N + 20],prime[N / 2],cnt;
ll add[maxn];
int n,m,a[maxn];

void init(){
    rep(i,2,N){
        if ( !tag[i] ) prime[++cnt] = i , phi[i] = i - 1;
        for (register int j = 1 ; j <= cnt && prime[j] * i <= N ; j++){
            tag[i * prime[j]] = 1;
            if ( (i % prime[j]) == 0 ){ phi[i * prime[j]] = phi[i] * prime[j]; break; }
            phi[i * prime[j]] = phi[i] * (prime[j] - 1);    
        }
    }
}
inline void modify(int id,int d){
    for (register int i = id ; i <= n ; i += lowbit(i)) add[i] += d;
}
inline ll query(int id){
    ll res = 0;
    for (register int i = id ; i ; i -= lowbit(i)) res += add[i];
    return res;
}
inline ll power(ll x,ll y,ll p){
    ll res = 1; int tag = 0,tagx = 0;
    while ( y ){
        if ( y & 1 ){
            res = res * x;
            tag |= tagx;
            if ( res >= p ) res %= p , tag = 1;
        }
        if ( x >= p ) tagx = 1 , x %= p;
        x = x * x;
        if ( x >= p ) tagx = 1 , x %= p;
        y >>= 1;
    }
    return res + (tag ? p : 0);
}
inline ll solve(int id,int r,ll p){
    if ( p == 1 ) return 1;
    if ( id > r ) return 1;
    ll cur = query(id) + a[id],y = solve(id + 1,r,phi[p]);
    return power(cur,y,p);
}   
int main(){
    //freopen("input.txt","r",stdin);
    init();
    scanf("%d %d",&n,&m);
    rep(i,1,n) a[i] = read();
    rep(i,1,m){
        int tp = read(),l = read(),r = read(),x = read();
        if ( tp == 1 ) modify(l,x) , modify(r + 1,-x);
        else{
            printf("%lld\n",solve(l,r,x) % x);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值