P3934 [Ynoi2016]炸脖龙I 树状数组 扩展欧拉定理

P3934 [Ynoi2016]炸脖龙I

题目链接

​ 树状数组 + 扩展欧拉定理。

​ 对于修改,直接树状数组差分。

​ 对于询问,我们设\(s(l, r, p)\)代表区间\([l, r]\)的询问。所以递归下去就可以做\(s(l, r, p) = a[l]^{s(l + 1, r, \phi(p) + \phi(p))} \mod p\)(假设\(s(l, r, p)\)都大于\(\phi(p)\)),这是扩展欧拉定理。

​ 对于phi我们直接预处理出\([1,2e7]\)的就好了。然后分情况:

​ 只要存在一个\(i <= r,a[i] = 1\),那么\(s(l, r, p) = s(l, i, p)\),因为1的任何次方都是1。

​ 对于一个区间,如果这个区间的长度大于5,并且没有一个1,那么它肯定可以往下递归,因为它肯定大于\(p\)\(2 ^ {2 ^ {2 ^ {2 ^ 2}}} = 2^{65536} > 2e7 = p\)

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

const int N = 5e5 + 5, M = 2e7 + 5;
int n, m, cnt;
int prime[M], is_prime[M], phi[M];
long long a[N], t[N];

void make_phi() {
    phi[1] = 1;
    for(int i = 2;i <= M - 5; i++) {
        if(!is_prime[i]) prime[++cnt] = i, phi[i] = i - 1;
        for(int j = 1;j <= cnt && i * prime[j] <= M - 5; j++) {
            is_prime[i * prime[j]] = 1;
            if(!(i % prime[j])) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }
}

long long ksm(long long x, long long y, long long mod) {
    long long res = 1; x %= mod; // 一定要加这一句!!!!!
    while(y) {
        if(y & 1) res = 1ll * x * res % mod;
        x = 1ll * x * x % mod; y >>= 1;
    }
    return res % mod;
}

int lowbit(int x) { return x & -x; }

void change(int x, long long val) { for(; x <= N - 5; x += lowbit(x)) t[x] += val; }

long long ask(int x) { 
    long long res = 0; for(; x ; x -= lowbit(x)) res += t[x]; return res; }

long long query(int l, int r, int p) {
    if(ask(l) % p == 0) return 0;
    else if(p == 1) return 1;
    else if(l == r) return ask(l) % p + (ask(l) >= p ? p : 0);   
    int rs = min(r, l + 5);
    for(int i = l + 1;i <= rs; i++) if(ask(i) == 1) { rs = i; break; }
    long long la = ask(rs);
    for(int i = rs - 1;i >= l + 1; i--) {
        long long tmp = la, now = ask(i); la = 1;
        while(tmp --> 0) {
            la *= now;
            if(la > phi[p]) {
                return ksm(ask(l), query(l + 1, r, phi[p]) + phi[p], p);
            }
        }
    }
    return ksm(ask(l), la, p);
}

int main() {

    n = read(); m = read(); make_phi();
//    for(int i = 1;i <= 10; i++) cout << i << " " << phi[i] << endl;
    for(int i = 1;i <= n; i++) a[i] = read(), change(i, a[i] - a[i - 1]);
    for(int i = 1, opt, l, r;i <= m; i++) {
        opt = read(); l = read(); r = read(); 
        long long x = read();
        if(opt == 1) change(l, x), change(r + 1, -x);
        if(opt == 2) printf("%lld\n", query(l, r, x) % x);
    }

    fclose(stdin); fclose(stdout); 
    return 0;
}
posted @ 2020-09-23 06:23  C锥  阅读(141)  评论(0编辑  收藏  举报