HDU 5634 Rikka with Phi(线段树)

题目链接:点击打开链接

题意:有3种区间操作, 1是把区间内的所有数变成它的欧拉函数值, 2是把区间所有数都变成一个数x,3是查询区间和。

思路:后两个操作就是线段树的区间修改和求和, 没什么好说的。 

题解说用平衡树(弱不会), 不过大致思路线段树同样可以维护, 因为一个数进行最多phiO(logn)次就会变成1, 所以我们可以在递归结束,向上传标记的时候顺便看看其子区间是不是都等于1, 是就合并起来, 给这个点重新标记。 

细节参见代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
typedef long double ld;
const ld EPS = 1e-9, PI = 3.1415926535897932384626433832795;
const int mod = 1000000000 + 7;
const int INF = int(1e9);
const ll INF64 = ll(1e18);
const int maxn = 300000 + 10;
int T, n, m, v, id, l, r, x;
ll sum[maxn<<2], setv[maxn<<2];
#define Max 10000010
ll euler[Max] = {0};
void Init(){
     euler[1]=1;
     for(ll i=2;i<Max;i++)
       euler[i]=i;
     for(ll i=2;i<Max;i++)
        if(euler[i]==i)
           for(ll j=i;j<Max;j+=i)
              euler[j]=euler[j]/i*(i-1);
}
void PushUp(int o) {
    sum[o] = sum[o<<1] + sum[o<<1|1];
    if(setv[o<<1] == setv[o<<1|1]) setv[o] = setv[o<<1];
    else setv[o] = 0;
}
void build(int l, int r, int o) {
    int m = (l + r) >> 1;
    sum[o] = 0;
    setv[o] = 0;
    if(l == r) {
        scanf("%d",&v);
        sum[o] = setv[o] = v;
        return ;
    }
    build(l, m, o<<1);
    build(m+1, r, o<<1|1);
    PushUp(o);
}
void pushdown(int l, int r, int o) {
    if(setv[o]) {
        int m = (l + r) >> 1;
        setv[o<<1] = setv[o<<1|1] = setv[o];
        sum[o<<1] = (ll)(m - l + 1) * setv[o];
        sum[o<<1|1] = (ll)(r - m) * setv[o];
        setv[o] = 0;
    }
}
void update(int L, int R, int v, int l, int r, int o) {
    int m = (l + r) >> 1;
    if(L <= l && r <= R) {
        setv[o] = (ll)v;
        sum[o] = (ll)v * (r - l + 1);
        return ;
    }
    pushdown(l, r, o);
    if(L <= m) update(L, R, v, l, m, o<<1);
    if(m < R) update(L, R, v, m+1, r, o<<1|1);
    PushUp(o);
}
void haha(int L, int R, int l, int r, int o) {
    int m = (l + r) >> 1;
    if(setv[o] && L <= l && r <= R) {
        setv[o] = euler[setv[o]];
        sum[o] = setv[o] * (r - l + 1);
        return ;
    }
    if(l == r) return ;
    pushdown(l, r, o);
    if(L <= m) haha(L, R, l, m, o<<1);
    if(m < R) haha(L, R, m+1, r, o<<1|1);
    PushUp(o);
}
ll query(int L, int R, int l, int r, int o) {
    int m = (l + r) >> 1;
    if(L <= l && r <= R) {
        return sum[o];
    }
    pushdown(l, r, o);
    ll ans = 0;
    if(L <= m) ans += query(L, R, l, m, o<<1);
    if(m < R) ans += query(L, R, m+1, r, o<<1|1);
    PushUp(o);
    return ans;
}
int main() {
    Init();
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&m);
        build(1, n, 1);
        while(m--) {
            scanf("%d%d%d",&id,&l,&r);
            if(id == 1) haha(l, r, 1, n, 1);
            else if(id == 2) {
                scanf("%d",&x);
                update(l, r, x, 1, n, 1);
            }
            else printf("%I64d\n",query(l, r, 1, n, 1));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值