P3747 [六省联考 2017] 相逢是问候 题解

Description

Luogu传送门

Solution

不打算详细写了,简单写写做题历程。

一看题,显然要用线段树维护,但是 \(c^{a_i}\) 这东西怎存??

于是看了一眼标签,发现 欧拉公式 这个东西,于是想到欧拉定理。

再联想到区间开方的操作只有前 \(\sqrt n\) 次有用。发现这东西只有前 \(\log\) 次操作有用,所以直接暴力维护即可。

另外 \(c^x\) 次方要提前预处理出来,不然会多一个 \(\log\)

其他的见代码吧,有一点注释。

这道题的思路还是非常有启发性的。

Code

#include <bits/stdc++.h>
#define ll long long
#define ls rt << 1
#define rs rt << 1 | 1

using namespace std;

namespace IO{
    inline int read(){
        int x = 0;
        char ch = getchar();
        while(!isdigit(ch)) ch = getchar();
        while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
        return x;
    }

    template <typename T> inline void write(T x){
        if(x > 9) write(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace IO;

const int N = 5e4 + 10;
int n, m, mod, c;
int a[N][60];

namespace Prework{
    int c1[60][1 << 15], c2[60][1 << 15], phi[60];
    int totp = 0;

    inline int add(int x) {return x >= mod ? x -= mod : x;}
    inline int times(ll x, int mod) {return x >= mod ? (x % mod + mod) : x;}
    inline int power(int x, int i) {return times(1ll * c1[i][x & ((1 << 15) - 1)] * c2[i][x >> 15], phi[i]);}

    //单点计算 phi
    inline int calc_phi(int x){
        int 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) res = res / x * (x - 1);
        return res;
    }

    //预处理 (c^x) % phi[i],分成两半(更快),对于每个 phi[i] 都要预处理
    inline void calc_c(){
        for(int i = 0; i <= totp; ++i){
            c1[i][0] = c2[i][0] = 1;
            for(int j = 1; j < (1 << 15); ++j)
                c1[i][j] = times(1ll * c1[i][j - 1] * c, phi[i]);
            c2[i][1] = times(1ll * c1[i][(1 << 15) - 1] * c, phi[i]);
            for(int j = 2; j < (1 << 15); ++j)
                c2[i][j] = times(1ll * c2[i][j - 1] * c2[i][1], phi[i]);
        }
    }

    //预处理出 a[i] 每次操作之后数是多少
    inline int calc(int x, int cnt, int i){
        if(!cnt) return times(x, phi[i]);
        if(i == totp) return c ? 1 : 0;
        return power(calc(x, cnt - 1, i + 1), i);
    }

    //同上
    inline void prework(){
        phi[0] = mod;
        while(phi[totp] > 1) totp++, phi[totp] = calc_phi(phi[totp - 1]);
        calc_c();
        for(int i = 1; i <= n; ++i){
            a[i][0] = read();
            for(int j = 1; j <= totp + 1; ++j){
                a[i][j] = calc(a[i][0], j, 0) % mod;
            }
            a[i][0] %= mod;
        }
    }
}
using namespace Prework;

namespace Segment_Tree{
    int sum[N << 2], mins[N << 2];//sum 记录区间和,mins 记录修改次数

    inline void pushup(int rt){
        mins[rt] = min(mins[ls], mins[rs]);
        sum[rt] = add(sum[ls] + sum[rs]);
    }

    inline void build(int l, int r, int rt){
        if(l == r){
            sum[rt] = a[l][0];
            return;
        }
        int mid = (l + r) >> 1;
        build(l, mid, ls);
        build(mid + 1, r, rs);
        pushup(rt);
    }

    inline void update(int L, int R, int l, int r, int rt){
        if(mins[rt] > totp) return;
        if(l == r) return sum[rt] = a[l][++mins[rt]], void();
        int mid = (l + r) >> 1;
        if(L <= mid) update(L, R, l, mid, ls);
        if(R > mid) update(L, R, mid + 1, r ,rs);
        pushup(rt);
    }

    inline int query(int L, int R, int l, int r, int rt){
        if(L <= l && r <= R) return sum[rt];
        int mid = (l + r) >> 1;
        int res = 0;
        if(L <= mid) res = add(res + query(L, R, l, mid, ls));
        if(R > mid) res = add(res + query(L, R, mid + 1, r, rs));
        return res;
    }
}
using namespace Segment_Tree;

signed main(){
    n = read(), m = read(), mod = read(), c = read();
    prework();
    build(1, n, 1);
    while(m--){
        int op = read(), l = read(), r = read();
        if(!op) update(l, r, 1, n, 1);
        else write(query(l, r, 1, n, 1)), puts("");
    }
    return 0;
}

\[\_EOF\_ \]

posted @ 2021-12-22 15:51  xixike  阅读(63)  评论(0编辑  收藏  举报