zoukankan      html  css  js  c++  java
  • 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\_ \]

  • 相关阅读:
    ORACLE创建数据库时无法创建目录
    KindEditor:Ajax提交表单时获取不到HTML内容
    mysql重置root密码
    假设检验-单样本检验
    推论统计分析-如何避免偏见和抽样分布
    推论统计分析1
    共享单车数据分析
    Kaggle泰坦尼克号生存情况预测
    R-长尾词练习
    R期望
  • 原文地址:https://www.cnblogs.com/xixike/p/15719856.html
Copyright © 2011-2022 走看看