HDU-4027(线段树)

HDU - 4027 The 36th ACM/ICPC Asia Regional Shanghai Site

题目链接:hdu-4027.

题目描述

一个长度为 n n n 的整数序列 a 1 , a 2 , . . . a n a_{1},a_{2},...a_{n} a1,a2,...an. 有 m m m个操作,每个操作为 s i , l i , r i s_{i}, l_{i},r_{i} si,li,ri
操作分为两种,当 s i s_{i} si 0 0 0时,将序列 a a a中区间 [ l i , r i ] [l_{i},r_{i}] [li,ri]中所有元素x更新为 x \sqrt{x} x ; 当 s i s_{i} si 1 1 1时,对序列 a a a中区间 [ l i , r i ] [l_{i},r_{i}] [li,ri]中所有元素求和并输出。
序列 a a a 的元素和不超过 2 63 2^{63} 263.

思路

正确思路
求区间和很简单。
开方操作:因为每次操作都是开根号,对于一个数来讲,一直开根号到 1 1 1,再往后开根号始终是 1 1 1. 即使最大的数, 2 63 2^{63} 263只需要开 6 6 6次根号就会到 1 1 1. 所以,即使暴力将每个数都更新到 1 1 1,也不会有太多操作,每个数最多被更新几次,不过不同的区间叠加可能会使操作多几次。
精髓所在:如果待开方的某区间全为 1 1 1,不必再递归。
注意点:
① 通过提交式测试法,发现数组元素最小值为 1 1 1 ,所以终止的判断条件为 t [ i ] . s u m = = t [ i ] . r − t [ i ] . l + 1 t[i].sum == t[i].r - t[i].l + 1 t[i].sum==t[i].rt[i].l+1. 如果有 0 0 0 的话,可以写 t [ i ] . s u m ≤ t [ i ] . r − t [ i ] . l + 1 t[i].sum \leq t[i].r - t[i].l + 1 t[i].sumt[i].rt[i].l+1.
② 序列 a a a的元素值要用 l o n g   l o n g long\,long longlong.
③ 还有就是这个格式问题,每组数据之间有个空行。
其(wo)他(de)思路
发现最大值最多开 6 6 6次到 1 1 1之后,思路就停滞了,一直在想结点应该记录什么信息,能方便开方操作,其实啥也不用记录,直接更新到叶结点就完事了。

代码

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)

const int MAXN = 100005;

LL a[MAXN];
struct node {
    int l, r;
    LL sum;
} t[MAXN * 4];
void push_up(int u) {
    t[u].sum = t[lson(u)].sum + t[rson(u)].sum;
    return;
}
void build(int l, int r, int u) {
    t[u].l = l;
    t[u].r = r;
    if(l == r) {
        t[u].sum = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, lson(u));
    build(mid+1, r, rson(u));
    push_up(u);
    return;
}
void update(int L, int R, int u) {
    if(t[u].l == t[u].r) {
        t[u].sum = sqrt(t[u].sum);
        return;
    }
    if(t[u].sum <= t[u].r - t[u].l + 1) return; // *QQ*
    int mid = (t[u].l + t[u].r) >> 1;
    if(mid >= L) update(L, R, lson(u));
    if(mid < R) update(L, R, rson(u));
    push_up(u);
    return;
}
LL Query(int L, int R, int u) {
    if(L <= t[u].l && R >= t[u].r) {
        return t[u].sum;
    }
    int mid = (t[u].l + t[u].r) >> 1;
    LL sum = 0;
    if(mid >= L) sum += Query(L, R, lson(u));
    if(mid < R) sum += Query(L, R, rson(u));
    return sum;
}
int main() {
    int n, m, s, L, R;
    int tt = 1;
    while(scanf("%d", &n) != EOF) {
        printf("Case #%d:\n",tt);
        tt++;
        for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); // WA n 
        build(1,n,1);
        scanf("%d", &m);
        for(int i = 1; i <= m; i++) {
            scanf("%d%d%d",&s, &L, &R);
            if(L > R) swap(L,R);
            if(s == 0) update(L, R, 1);
            else printf("%lld\n",Query(L, R, 1));
        }
        printf("\n"); // WA 2
    }    
    return 0;    
}
/*
2^63  6次 
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值