[Shoi2017]相逢是问候(拓展欧拉公式)

[Shoi2017]相逢是问候(拓展欧拉公式)

题面

这题和依旧是拓展欧拉定理

注意到phi的只要log次就能修改成1,因为

gcd(n,x)==gcd(n, n-x)

所以n>=3时,phi(n)为偶数,而偶数的phi显然至少除2

对了这里判断x和phi(p)的大小时,我是记录了一个flag,表示是否取过mod,去过则大于,返回x%phi(p)+phi(p)

每个点只会涉及到log(n)次的修改,网上搜了下大家都是用线段树

比较懒qwq 感觉线段树比较难敲,想了想了发现做过类似的题,可以用树状数组加并查集水过去。
每个点维护一下从它到最后 最前能修改的点是谁 然后每次修改,树状数组维护

最近debug能力大幅下降感觉,这都改了好久qwq

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 51000;
int n, m, p, c;
struct Tbit{
    int b[maxn], m;
    void clear(int size){ m = size; memset(b, 0, sizeof(b)); }
    void modify(int x, int ad){
        if (ad < 0) ad += p;
        for (int i = x; i <= m; i += i&-i)
            b[i] += ad, b[i] %= p;
    }
    int sum(int x){
        int ans = 0;
        for (int i = x; i; i -= i&-i)
            ans += b[i], ans %= p;
        return ans;
    }
    int sum(int x, int y){ return s#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 151000;
int n, m, p, c;
struct Tbit{
    int b[maxn], m;
    void clear(int size){ m = size; memset(b, 0, sizeof(b)); }
    void modify(int x, int ad){
        if (ad < 0) ad += p;
        for (int i = x; i <= m; i += i&-i)
            b[i] += ad, b[i] %= p;
    }
    int sum(int x){
        int ans = 0;
        for (int i = x; i; i -= i&-i)
            ans += b[i], ans %= p;
        return ans;
    }
    int sum(int x, int y){ return ((sum(y) - sum(x - 1))%p + p) % p; }
}bit;
struct Tunf{
    int fa[maxn];
    void clear(int size){
        for (int i = 1; i <= size; i ++)
            fa[i] = i;
    }
    int find(int x){
        if (x == fa[x]) return x;
        return fa[x] = find(fa[x]);
    }
    void cc(int x, int y){ fa[find(x)] = find(y); }
}unf;
int tmt[maxn], a[maxn], b[maxn];
void init(){
    scanf("%d%d%d%d", &n, &m, &p, &c);
    bit.clear(n);
    for (int i = 1; i <= n; i ++){
        scanf("%d", &a[i]);
        bit.modify(i, b[i] = a[i]);
    }
}
map<int, int> PHI;
int phi(int x){
    if (x == 1) return 1;
    if (PHI[x]) return PHI[x];
    int ans = x, y = x;
    for (int i = 2; i * i <= x; i ++) if (x % i == 0){
        ans = 1LL * ans * (i - 1) / i;
        while (x % i == 0) x /= i;
    }
    if (x != 1) ans = 1LL * ans * (x - 1) / x;
    return PHI[y] = ans;
}
int pw(int x, int i, int p, bool &flag){
//  cout <<"PW" << ' ' <<x << ' '<<i<<endl;
    int k = x; ll ans = 1;
    while (i){
        if (i&1){
            ans = ans * k;
            if (ans >= p) flag = 1, ans %= p;
        }
        i >>= 1;
        if (i) flag |= (1LL * k * k >= p);
        k = 1LL * k * k % p;
    }
    return ans;
}
int func(int x, int dep, int p){
    if (c == 1) return 1;
    if (dep == 0){
//      cout <<x <<' ' <<p <<' ' <<x % p + (x >= p?p:0) <<endl;
        return x % p + (x >= p?p:0);
    }
    bool flag = 0;
    int asd = 0;
    int tmp = pw(c, asd = func(x, dep - 1, phi(p)), p, flag);
//  cout <<x <<' ' <<asd <<' ' <<dep <<' ' <<tmp <<' ' <<flag <<' ' <<p <<' '<<phi(p) <<endl;
    return tmp + (flag?p:0);
}
void work(){
    unf.clear(n);
    int mxlen = 2, tmp = p;
    while (tmp > 1) tmp = phi(tmp), mxlen ++;
//  cout <<mxlen <<endl;
    for (int i = 1; i <= m; i ++){
        int op, l, r; scanf("%d%d%d", &op, &l, &r);
        if (op == 0){
            int nw = unf.find(l);

            while (nw <= r && nw){
        //      cout <<nw <<endl;
                int tmp = func(a[nw], ++ tmt[nw], p);
                tmp %= p;
            //  cout <<tmp <<' ' <<b[nw] <<endl;
                bit.modify(nw, tmp - b[nw]);
                b[nw] = tmp;
                if (tmt[nw] == mxlen + 1) unf.cc(nw, nw + 1);
                nw = unf.find(nw + 1);
            }
        }else printf("%d\n", bit.sum(l, r));
    }
}
int main(){
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值