HDU 5828 Rikka with Sequence(线段树)

题目链接:点击打开链接

思路:

对于该题, 由于存在区间加一个值, 那么所有数都可能永远不会变成1, 但是数与数之间的相对值会趋近于相等。  比如1 2 3 4 5, 进行一次根号操作变成1 1 1 2 2, 而一旦如果相等, 那么他们同时加一个数也是相等的。  所以我们增加一个标记bit[rt]表示该区间内的数是否全部相等,如果相等等于什么。

细节参见代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
using namespace std;
typedef long long LL;
const int maxn = 100010;
LL sum[maxn<<2], add[maxn<<2];
int n, m, vv, bit[maxn<<2];
inline void PushDown(int rt,int m) {
       if (add[rt]) {
              add[rt<<1] += add[rt];
              add[rt<<1|1] += add[rt];
              if(bit[rt<<1]) bit[rt<<1] += add[rt];
              if(bit[rt<<1|1]) bit[rt<<1|1] += add[rt];

              sum[rt<<1] += add[rt] * (m - (m >> 1));
              sum[rt<<1|1] += add[rt] * (m >> 1);
              add[rt] = 0;
       }
}
inline void PushUp(int rt) {
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    if(bit[rt<<1] == bit[rt<<1|1] && bit[rt<<1] && bit[rt<<1|1]) bit[rt] = bit[rt<<1];
    else bit[rt] = 0;
    return ;
}
inline void build(int l, int r, int rt) {
    sum[rt] = 0;
    add[rt] = 0;
    bit[rt] = 0;
    if(l == r) {
        scanf("%d", &vv);
        sum[rt] = vv;
        bit[rt] = vv;
        return ;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}
inline void update(int L, int R, int l, int r, int rt) {
    if(L<=l && r<=R && bit[rt]) {
        LL res = sqrt(bit[rt] + 0.5);
        add[rt] -= bit[rt] - res;
        sum[rt] = (LL)(r - l + 1) * res;
        bit[rt] = res;
        return ;
    }
    if(l == r) {
        sum[rt] = sqrt(sum[rt] + 0.5);
        return ;
    }
    int m = (l+r)>>1;
    PushDown(rt , r - l + 1);
    if(L <= m) update(L, R, lson);
    if(m < R) update(L, R, rson);
    PushUp(rt);
}
inline void update2(int L,int R,int c,int l,int r,int rt) {
       if (L <= l && r <= R) {
            add[rt] += c;
            if(bit[rt]) bit[rt] += c;
            sum[rt] += (LL)c * (r - l + 1);
            return ;
       }
       PushDown(rt , r - l + 1);
       int m = (l + r) >> 1;
       if (L <= m) update2(L , R , c , lson);
       if (m < R) update2(L , R , c , rson);
       PushUp(rt);
}
inline LL query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) {
        return sum[rt];
    }
    int m = (l+r)>>1;
    PushDown(rt , r - l + 1);
    LL ans = 0;
    if(L <= m) ans += query(L, R, lson);
    if(m < R) ans += query(L, R, rson);
    return ans;
}
int id[maxn], l[maxn], r[maxn], v[maxn];

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);

        scanf("%d", &m);
        build(1, n, 1);
        int last = -1;
        for(int i = 1; i <= m; i++) {
            scanf("%d%d%d", &id[i], &l[i], &r[i]);
            if(l[i] > r[i]) swap(l[i], r[i]);
            if(id[i] == 1) scanf("%d", &v[i]);
            if(id[i] == 3) last = i;
        }
        for(int i = 1; i <= m; i++) {
            if(last < i) break;
            if(id[i] == 1) {
                update2(l[i], r[i], v[i], 1, n, 1);
            }
            else if(id[i] == 2) {
                update(l[i], r[i], 1, n, 1);
            }
            else {
                printf("%I64d\n", query(l[i], r[i], 1, n, 1));
            }
        }
    }
    return 0;
}
时间: 2024-10-07 09:09:04

HDU 5828 Rikka with Sequence(线段树)的相关文章

HDU 5828 Rikka with Sequence (线段树+剪枝优化)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5828 给你n个数,三种操作.操作1是将l到r之间的数都加上x:操作2是将l到r之间的数都开方:操作3是求出l到r之间的和. 操作1和3就不说了,关键是开方操作. 一个一个开方,复杂度太高,无疑会T.所以我们来剪枝一下. 我们可以观察,这里一个数最多开方4,5次(loglogx次)就会到1,所以要是一段区间最大值为1的话,就不需要递归开方下去了.这是一个剪枝. 如果一段区间的数都是一样大小(最大值等于

hdu 4893 Wow! Such Sequence!(线段树)

题目链接:hdu 4983 Wow! Such Sequence! 题目大意:就是三种操作 1 k d, 修改k的为值增加d 2 l r, 查询l到r的区间和 3 l r, 间l到r区间上的所以数变成最近的斐波那契数,相等的话取向下取. 解题思路:线段树,对于每个节点新增一个bool表示该节点以下的位置是否都是斐波那契数. #include <cstdio> #include <cstring> #include <cstdlib> #include <algor

判断相同区间(lazy) 多校8 HDU 5828 Rikka with Sequence

1 // 判断相同区间(lazy) 多校8 HDU 5828 Rikka with Sequence 2 // 题意:三种操作,1增加值,2开根,3求和 3 // 思路:这题与HDU 4027 和HDU 5634 差不多 4 // 注意开根号的话,遇到极差等于1的,开根号以后有可能还是差1.如 5 // 2 3 2 3... 6 // 8 9 8 9... 7 // 2 3 2 3... 8 // 8 9 8 9... 9 // 剩下就是遇到区间相等的话,就直接开根号不往下传 10 11 12

2016暑假多校联合---Rikka with Sequence (线段树)

2016暑假多校联合---Rikka with Sequence (线段树) Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them: Yuta has an array A with n numbers. Then he make

HDU 5828 Rikka with Sequence(线段树 开根号)

Rikka with Sequence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2777    Accepted Submission(s): 503 Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situati

HDU 6089 Rikka with Terrorist (线段树)

题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=6089 题解 这波强行维护搞得我很懵逼... 扫描线,只考虑每个点能走到左上方(不包括正上方,但包括正左方)的哪些点,然后旋转四次坐标系处理 所有询问和操作点按照先\(x\)后\(y\)坐标的顺序排序,然后枚举每一行,按\(y\)从小到大的顺序枚举这一行每个点 对于一个询问点找出前面最后一个操作点,那么要求的就是一个矩形减去一个区间内所有后缀最大值的和 然后这个东西可以用线段树直接维护,记录个区间最大

HDU 5828 Rikka with Sequence

好久没写线段树了,这题作为一个回味.. 第一种操作的话,就是一个延迟标记. 第二种操作可以暴力更新下去,但是有一个优化,如果某区间内所有值都是一样的,那么到此结束,不要继续往下面暴力更新了. 这样一来的话,pushDown的时候要注意一下,如果该区间内所有值都一样,那么延迟标记不要往下扔了,直接把该区间的信息传下去.如果该区间内所有值不一样,将延迟标记扔下去. 总体难度不算大,仔细一些就能AC. 不过这样的方法是水过去的. 10W个2 3 2 3 2 3 2 3..... 10W次操作 +6 s

HDU 5634 Rikka with Phi 线段树

题意:bc round 73 div1 D 中文题面 分析:注意到10^7之内的数最多phi O(log(n))次就会变成1, 因此可以考虑把一段相同的不为1的数缩成一个点,用平衡树来维护. 每次求phi的时候就在平衡树上取出这个区间然后暴力求phi,如果一段数变成了1, 就在平衡树里面删掉它,最后统计答案的时候只要把区间中被删去的1加回答案即可, 时间复杂度O((n + m)logn) 注:平衡树,写起来麻烦(然后其实我也不会写) 但是题解当中说把一段相同的数缩成一个点,就很好 所以用线段树,

HDU 4893 Wow! Such Sequence 线段树暴力

题意:n个数 初始为0,三个操作:给某个数加上d,查询区间和,把区间[l,r]中每个a[i]变为离a[i]最近的斐波那契数,n,m<=1e5. 无操作1情况下,每个点最多变化一次,每次修改logn,算上操作1 最坏情况下修改n+m次 O((n+m)logn). 对区间设个标记 线段树暴力即可. #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1e9+7; const int