HDU 5828 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 makes m operations on it.

There are three type of operations:

1 l r x : For each i in [l,r], change A[i] to A[i]+x
2 l r : For each i in [l,r], change A[i] to ⌊A−−√[i]⌋
3 l r : Yuta wants Rikka to sum up A[i] for all i in [l,r]

It is too difficult for Rikka. Can you help her?

Input
The first line contains a number t(1<=t<=100), the number of the testcases. And there are no more than 5 testcases with n>1000.

For each testcase, the first line contains two numbers n,m(1<=n,m<=100000). The second line contains n numbers A[1]~A[n]. Then m lines follow, each line describe an operation.

It is guaranteed that 1<=A[i],x<=100000.

Output
For each operation of type 3, print a lines contains one number – the answer of the query.

Sample Input
1
5 5
1 2 3 4 5
1 3 5 2
2 1 4
3 2 4
2 3 5
3 1 5

Sample Output
5
6

两个基本线段树题的融合,成段更新+hdu4027,坑点在于这个开根号到1之后还会有加的操作,如果单纯像4027那样只对一个是否区间都为1剪枝会超时,增加剪枝:

区间内数都为1后,再有操作时区间内数字都相同,
也就是当某区间内值都相同时,各种操作可以只算到这层,然后把lazy标记向下推一层;

参考网上大神完善了代码,比赛时并没能想出来怎么优化TAT。。。好菜啊。。。

ac代码:

#include <bits/stdc++.h>

using namespace std;

#define rep(i,a,n) for(int i = (a); i < (n); i++)
#define per(i,a,n) for(int i = (n)-1; i >= (a); i--)
#define clr(arr,val) memset(arr, val, sizeof(arr))
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define pi acos(-1)
typedef pair<int, int> pii;
typedef long long LL;
const double eps = 1e-8;
const int mod = 1000000007;
#define lson l,mid,rt*2
#define rson mid+1,r,rt*2+1
const int maxn = 100005;

LL sum[maxn<<2], laz[maxn<<2];
LL mark[maxn<<2];
void PushUp(int rt){
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    if(mark[rt<<1] == mark[rt<<1|1])
        mark[rt] = mark[rt<<1];
    else mark[rt] = 0;
    return ;
}
void pushdown(int rt, int len){
    if(mark[rt]){
        mark[rt<<1] = mark[rt<<1|1] = mark[rt];
        sum[rt<<1] = mark[rt]*(len-len/2);
        sum[rt<<1|1] = mark[rt]*(len/2);
        laz[rt] = 0;
    }
    if(laz[rt]){
        laz[rt<<1] += laz[rt];
        laz[rt<<1|1] += laz[rt];
        sum[rt<<1] += laz[rt]*(len-len/2);
        sum[rt<<1|1] += laz[rt]*(len/2);
        if(mark[rt<<1]) mark[rt<<1] += laz[rt];
        if(mark[rt<<1|1]) mark[rt<<1|1] += laz[rt];
        laz[rt] = 0;
    }
}
void build(int l, int r, int rt){
    laz[rt] = 0;
    mark[rt] = 0;
    if(l == r){
        scanf("%I64d", &sum[rt]);
        mark[rt] = sum[rt];
        return ;
    }
    int mid = (l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}
void add(int L,int R,LL c,int l,int r,int rt){
    if(L <= l && R >= r){
        sum[rt] += c*(r-l+1);
        laz[rt] += c;
        if(mark[rt]) mark[rt] += c;
        return;
    }
    pushdown(rt, r-l+1);
    int mid = (l+r)>>1;
    if(L<=mid)
        add(L,R,c,lson);
    if(mid < R)
        add(L,R,c,rson);
    PushUp(rt);
    return ;
}
void sq(int L, int R, int l, int r, int rt){
    if(L <= l && R >= r && mark[rt]){
        mark[rt] = (LL)sqrt(mark[rt]);
        sum[rt] = mark[rt]*(r-l+1);
        return ;
    }
    pushdown(rt, r-l+1);
    int mid = (l+r)>>1;
    if(L <= mid) sq(L, R, lson);
    if(mid < R) sq(L, R, rson);
    PushUp(rt);
    return ;
}
LL query(int L, int R, int l, int r, int rt){
    if(L <= l && r <= R){
        return sum[rt];
    }
    pushdown(rt, r-l+1);
    int mid = (l+r)>>1;
    LL ans = 0;
    if(L <= mid) ans += query(L, R, lson);
    if(mid < R) ans += query(L, R, rson);
    return ans;
}
int main(int argc, char const *argv[]) {
    int n, m;
    int t;
    scanf("%d", &t);
    while(t--){
        scanf("%d%d", &n, &m);
        build(1, n, 1);
        int a, x, y;
        LL num;
        while(m--){
            scanf("%d%d%d", &a, &x, &y);
            if(a == 1){
                scanf("%I64d", &num);
                add(x, y, num, 1, n, 1);
            }
            if(a == 2)
                sq(x, y, 1, n, 1);
            if(a == 3)
                printf("%I64d\n", query(x, y, 1, n, 1));
        }
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值