2021 icpc第二场网络赛 L Euler Function

链接

大意

每次给你两种操作,一种是区间乘,另一种是求 p h [ a [ l ] ] + p h [ a [ l + 1 ] ] . . . + p h [ a [ r ] ] ph[a[l]] + ph[a[l+1]]...+ph[a[r]] ph[a[l]]+ph[a[l+1]]...+ph[a[r]]

思路

首先,观察到区间乘是套了一个函数的,所以我们不能直接乘,得想办法把这个化简一下

我们知道对于欧拉函数有这么一个性质, 当p是质数时

  • p不是x的因子时, p h ( p ∗ x ) = p ∗ p h ( x ) ph(p*x)=p*ph(x) ph(px)=pph(x)
  • p是x的因子时, p h ( p ∗ x ) = ( p − 1 ) ∗ p h ( x ) ph(p*x)=(p-1)*ph(x) ph(px)=(p1)ph(x)

所以给每段区间乘 w w w之前,需要先分解w,可以先打一个表,保存1-100的所有结果。

这样就成功把这个p从提取到了外面,但是不知道每次是乘(p-1)还是p,所以我们需要加一个状态st,表示这个区间有没有某个因子。100以内的质数不超过25个,所以只需要用一个int表示有没有某个因子就行了。某一位为1时,表示有这个质因子。

更新操作:

  • 当区间 [ l , r ] [l,r] [l,r]都有因子p时,给这个区间打上一个乘法标记,更新sum。
  • 否则则递归更新左右儿子

进行 25 ∗ 1 0 5 25* 10^5 25105 次,所有叶子节点都会包含了100以内的所有质数,所以复杂度完全ok

代码

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N = 1e5 + 10, M = N + N, B = 4, P = 998244353, INF = 1e9 + 10;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
using tp = tuple<int,int,int,int>;
bool muti = false;

int n, m;
int pr[N], phi[N], cnt;

int prid[200];
//存分解后的数字
vector<int> fac[200];
bool st[N];
int a[N];

struct Node
{
    int l, r;
    LL mul, sum;
    //质数状态
    int st;   
}tr[N << 2];

void get_primes(int n) {
    phi[1] = 1;
    for(int i = 2; i <= n; i++) {
        if(!st[i]) pr[cnt++] = i, phi[i] = i - 1, prid[i] = cnt - 1;
        for(int j = 0; pr[j] * i <= n; j++) {
            st[pr[j] * i] = true;
            if(i % pr[j] == 0) {
                phi[pr[j] * i] = pr[j] * phi[i];
                break;
            }
            phi[pr[j] * i] = (pr[j] - 1) * phi[i];
        }
    }
}
void pushup(int u) {
    tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % P;
    tr[u].st = tr[u << 1].st & tr[u << 1 | 1].st;
}

void pushdown(int u) {
    if(tr[u].mul > 1) {
        LL& v = tr[u].mul;
        auto& lch = tr[u << 1], &rch = tr[u << 1 | 1];

        lch.sum = lch.sum * v % P, lch.mul = lch.mul * v % P;
        rch.sum = rch.sum * v % P, rch.mul = rch.mul * v % P;

        v = 1;
    }
}

void build(int u, int l, int r) {
    tr[u] = {l, r, 1ll};
    if(l == r) {
        tr[u].sum = phi[a[l]];
        for(int x: fac[a[l]]) tr[u].st |= 1 << prid[x];
        return;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u); 
}
void printNode(int u) {
    auto rt = tr[u];
    printf("[%d, %d], sum=%lld, mul=%lld, st=%d\n\n", tr[u].l, tr[u].r, rt.sum, rt.mul, rt.st);
}
//区间乘操作
void update(int u, int l, int r, int val) {

    if(tr[u].l == tr[u].r) {
        if(tr[u].st & (1 << prid[val])) tr[u].sum = tr[u].sum * val % P;
        else tr[u].sum = tr[u].sum * (val - 1) % P;
        tr[u].st |= 1 << prid[val];
        return;
    }

    if(tr[u].l >= l && tr[u].r <= r) {
        if(tr[u].st & (1 << prid[val])) {
            tr[u].mul = tr[u].mul * val % P;
            tr[u].sum = tr[u].sum * val % P;
        }
        else {
            //更新左右子树
            pushdown(u);
            update(u << 1, l, r, val), update(u << 1 | 1, l, r, val);
            pushup(u);
        }
        return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    pushdown(u);
    if(l <= mid) update(u << 1, l, r, val);
    if(r > mid) update(u << 1 | 1, l, r, val);
    pushup(u);
}
int query(int u, int l, int r) {
    if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;

    int mid = tr[u].l + tr[u].r >> 1;
    int res = 0;
    pushdown(u);
    if(l <= mid) res = query(u << 1, l, r);
    if(r > mid) res += query(u << 1 | 1, l, r); 
    return res % P;
}
void solve() {
    get_primes(100);

    for(int i = 2; i <= 100; i++) {
        int x = i;
        for(int j = 0; j < cnt && pr[j] * pr[j] <= x; j++) {
            int c = 0;
            while(x % pr[j] == 0) {
                c++;
                x /= pr[j];
                fac[i].push_back(pr[j]);
            }
        }
        if(x > 1) fac[i].push_back(x);
    }

    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i];
    build(1, 1, n);

    while(m--) {
        int t, l, r, w;
        cin >> t >> l >> r;
        if(!t) {
            cin >> w;
            for(int x: fac[w]) update(1, l, r, x);
        }
        else cout << query(1, l, r) << endl;
    }
}


int main()
{
#ifdef ONLINE_JUDGE
#else 
    freopen("L.txt", "r", stdin);
#endif
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T = 1;
    
    if(muti) cin >> T;
    while (T--) solve();
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值