acmoj - 数蘑菇线段树区间更新

数蘑菇

题目描述


某土豪有许多种蘑菇的房子(编号从1到n,n<=10000),每个房子开始时有个蘑菇,大小在2到1000之间,每当洒脱的土豪给他们浇水时,这些蘑菇就会变成原来的平方倍大,比如一个大小为2的蘑菇在土豪浇过水之后就会变成4这么大,但是因为土豪住在帝都,每个房子只有1000平方米这么大,所以每个蘑菇在长到1000之后就不会再长大了(比如对于一个大小为50的蘑菇浇水,它也只能长大到1000),土豪每天喜欢骑着自行车(乡下人羡慕城里人开车溜达,城里的土豪却骑着自行车,你们城里人真会玩)给一连串的房子里的蘑菇浇水,从l到r,然后土豪又经常要查询某些房间的蘑菇一共有多少了,好联系卖家,聪明的你,能帮土豪这个忙吗?搞不好土豪一高兴随便送套房子给你呢!

输入描述


输入首先是一个T,表示一共有T组数据

下一行是一个n,代表土豪有几间房子

下一行是n个整数数,代表从1到n每个房间开始蘑菇的大小(<=1000)

下一行是一个整数k(1<=k<=10000),表示土豪浇水和数蘑菇的次数,

后面跟着k行,每行三个整数q,l,r(1<=l<=r<=10000)

q表示土豪的操作,

q为0的时候表示土豪给l到r这些房间里的蘑菇浇水,

q为1的时候表示土豪想要知道l到到r这些房间里的蘑菇总大小

输出描述


对于第n组数据首先输出一行"Case #n:",然后对于每次询问输出一个答案,每个答案占一行

输入样例

1
10
2 3 4 5 6 7 8 9 10 11
3
0 1 10
1 1 10
1 1 5

输出样例

Case #1:
505
90
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 10000 + 5;
int n, T, m;
int sum[maxn << 2],col[maxn << 2];
bool isupd[maxn << 2];//记录着他的子区间是否有要更新的
void inline pushup(int rt) {
    sum[rt] = sum[rt << 1] + sum[rt << 1|1];
    if(rt & 1)
        isupd[(rt + 1) / 2] = isupd[rt];//如果子有要更新的,就要标记父亲节点,告诉他子节点有更新
    else
        isupd[rt / 2] = isupd[rt];
}
void inline pushdown(int rt) {
    if(col[rt]) {
        col[rt << 1] += col[rt];
        col[rt << 1|1] += col[rt];
        col[rt] = 0;
    }
    isupd[rt << 1] = isupd[rt];
    isupd[rt << 1|1] = isupd[rt];
}
void build(int rt, int l, int r) {
    col[rt] = 0;
    isupd[rt] = 0;
    if(l == r) {
        scanf("%d", &sum[rt]);
        return;
    }
    int mid = (l + r) >> 1;
    build(rt << 1, l, mid);
    build(rt << 1|1, mid + 1, r);
    pushup(rt);
}
void update(int L, int R, int c, int rt, int l, int r) {
    if(L <= l && r <= R) {
        col[rt] += c;
        isupd[rt] = 1;
        return;
    }
    pushdown(rt);
    int mid = (l + r) >> 1;
    if(L <= mid) update(L, R, c, rt << 1, l, mid);
    if(R > mid) update(L, R, c,rt << 1|1, mid + 1, r);
    pushup(rt);
}
int query(int L, int R, int rt, int l, int r) {
    if(l == r) {
        if(sum[rt] == 1000)return sum[rt];//必须剪枝,否则超时
        if(isupd[rt]) {
            for(int i = 0; i < col[rt]; i ++) {
                sum[rt] = sum[rt] * sum[rt];
                if(sum[rt] >= 1000) sum[rt] = 1000;
            }
            col[rt] = 0;
            isupd[rt] = 0;
        }
        return sum[rt];
    }
    if(L <= l && r <= R) {
        int res = sum[rt];
        if(sum[rt] == 1000 * (r - l + 1))return sum[rt];//必须剪枝,否则超时
        if(isupd[rt]) {//如果子节点有更新,那么他的值就要重新通过求和来确定
            res = 0;
            int mid = (l + r) >> 1;
            pushdown(rt);
            res += query(l, r, rt << 1 , l, mid);
            res += query(l, r, rt << 1|1, mid + 1, r);
            isupd[rt] = 0;
        }
        return sum[rt] = res;
    }
    int mid = (l + r) >> 1;
    int res = 0;
    if(L <= mid)res += query(L, R, rt << 1, l, mid);
    if(R > mid) res += query(L, R, rt << 1|1, mid + 1, r);
    pushup(rt);
    return res;
}
int main() {
    int q, l, r, tt = 1;
    //freopen("D://imput.txt","r",stdin);
    scanf("%d", &T);
    while(T --) {
        scanf("%d", &n);
        build(1, 1, n);
        scanf("%d", &m);
        printf("Case #%d:\n",tt ++);
        for(int i = 0; i < m; i ++) {
            scanf("%d%d%d", &q, &l, &r);
            if(q) {
                printf("%d\n",query(l, r, 1, 1, n));
            } else {
                update(l, r, 1, 1, 1, n);
            }
        }
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-03 15:10:54

acmoj - 数蘑菇线段树区间更新的相关文章

POJ 2528 Mayor&#39;s posters (线段树区间更新+离散化)

题目链接:http://poj.org/problem?id=2528 给你n块木板,每块木板有起始和终点,按顺序放置,问最终能看到几块木板. 很明显的线段树区间更新问题,每次放置木板就更新区间里的值.由于l和r范围比较大,内存就不够了,所以就用离散化的技巧 比如将1 4化为1 2,范围缩小,但是不影响答案. 写了这题之后对区间更新的理解有点加深了,重点在覆盖的理解(更新左右两个孩子节点,然后值清空),还是要多做做题目. 1 #include <iostream> 2 #include <

HDU 4902 Nice boat(线段树 区间更新)

Nice boat 大意:给你一个区间,每次可以进行两种操作,1:把区间中的数全都变成x  2:把区间中大于x的数变成gcd(a[i], x),最后输出序列. 思路:线段树成段更行,用num数组的叶子存储数据,节点当作lazy来使用. 1 #include <stdio.h> 2 const int maxn = 100005; 3 4 int num[maxn<<2]; 5 6 int gcd(int a, int b){ 7 return b?gcd(b, a%b):a; 8

ZOJ - 1610 Count the Colors(线段树区间更新,单点查询)

1.给了每条线段的颜色,存在颜色覆盖,求表面上能够看到的颜色种类以及每种颜色的段数. 2.线段树区间更新,单点查询. 但是有点细节,比如: 输入: 2 0 1 1 2 3 1 输出: 1 2 这种情况就需要处理一下,代码中把所有的左端点都+1,避免了这种情况. 3. #include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define L(root) ((root) &l

HDU 4614 Vases and Flowers(线段树区间更新+二分)

Problem Description Alice is so popular that she can receive many flowers everyday. She has N vases numbered from 0 to N-1. When she receive some flowers, she will try to put them in the vases, one flower in one vase. She randomly choose the vase A a

poj 3468 A Simple Problem with Integers (线段树区间更新入门)

A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 70442   Accepted: 21723 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of

poj 2777 Count Color (线段树区间更新)

Count Color Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 37647   Accepted: 11315 Description Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem.

HDU 4902 Nice boat 2014杭电多校训练赛第四场F题(线段树区间更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4902 解题报告:输入一个序列,然后有q次操作,操作有两种,第一种是把区间 (l,r) 变成x,第二种是把区间 (l,r) 中大于x的数跟 x 做gcd操作. 线段树区间更新的题目,每个节点保存一个最大和最小值,当该节点的最大值和最小值相等的时候表示这个区间所有的数字都是相同的,可以直接对这个区间进行1或2操作, 进行1操作时,当还没有到达要操作的区间但已经出现了节点的最大值跟最小值相等的情况时,说明

线段树 + 区间更新: HDU 4893 Wow! Such Sequence!

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2234    Accepted Submission(s): 657 Problem Description Recently, Doge got a funny birthday present from his new friend, Prot

线段树区间更新,区间统计+离散化 POJ 2528 Mayor&#39;s posters

题意:有一个很长的板子(10000000长),在上面贴n(n<=10000)张海报,问最后从外面能看到几张不同的海报. 因为板子有10000000长,直接建树肯定会爆,所以需要离散化处理,对于每张海报,有两个端点值,最后能看到几张海报跟他们的端点值的相对大小有关,跟绝对大小无关,所以就把所有海报的端点离散化处理,总共2n个端点,排序去重,对应p(p<=2n)个点.然后建树,因为p不超过20000,所以这样就可以接受了.区间更新时,因为我们只关心最外面海报的颜色有多少种,所以向下传递节点信息的时