HDU - Transformation(线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4578
Time Limit: 15000/8000 MS (Java/Others) Memory Limit: 65535/65536 K (Java/Others)

Problem Description

Yuanfang is puzzled with the question below:
There are n integers, a1, a2, …, an. The initial values of them are 0. There are four kinds of operations.
Operation 1: Add c to each number between ax and ay inclusive. In other words, do transformation ak<—ak+c, k = x,x+1,…,y.
Operation 2: Multiply c to each number between ax and ay inclusive. In other words, do transformation ak<—ak×c, k = x,x+1,…,y.
Operation 3: Change the numbers between ax and ay to c, inclusive. In other words, do transformation ak<—c, k = x,x+1,…,y.
Operation 4: Get the sum of p power among the numbers between ax and ay inclusive. In other words, get the result of axp+ax+1p+…+ay p.
Yuanfang has no idea of how to do it. So he wants to ask you to help him.

Input

There are no more than 10 test cases.
For each case, the first line contains two numbers n and m, meaning that there are n integers and m operations. 1 <= n, m <= 100,000.
Each the following m lines contains an operation. Operation 1 to 3 is in this format: “1 x y c” or “2 x y c” or “3 x y c”. Operation 4 is in this format: “4 x y p”. (1 <= x <= y <= n, 1 <= c <= 10,000, 1 <= p <= 3)
The input ends with 0 0.

Output

For each operation 4, output a single integer in one line representing the result. The answer may be quite large. You just need to calculate the remainder of the answer when divided by 10007.

Sample Input

5 5
3 3 5 7
1 2 4 4
4 1 5 2
2 2 5 8
4 3 5 3
0 0

Sample Output

307
7489

Problem solving report:

Description: 一段长为n的区间,m次操作,四种操作:将[l, r]内的每个数加上x,将[l, r]内的每个数乘上x,将[l, r]内的每个数变成x,求出[l, r]内所有数的p次方的和。
Problem solving: 线段树,我们建立线段树的同时开个vis标记数组,如果该节点为1说明其所有子节点的值全部相同,否则不同。那么到查询的时候我们对于节点标记为1的并且在查询区间内的则可大大简化,由于该节点的所有子节点的值全部相同,则只需算出来一个的p次方,再乘以区间长度即可。这道题的关键点就是数组的初始值都一样,维护一个相同值区间,突破点就在于判断区间的所有数是否相同。

Accepted Code:

/* 
 * @Author: lzyws739307453 
 * @Language: C++ 
 */
#include <bits/stdc++.h>
using namespace std;
#define lson rt << 1
#define rson rt << 1 | 1
const int MAXN = 1e5 + 5;
const int MOD = 10007;

//线段树维护 结点的值 标记变量
struct Tree {
    int val;
    bool vis;//vis为标记变量,如果为真,说明此节点的所有子节点的值全部一样,否则不一样
}seg[MAXN << 2];

//建树
void Create(int l, int r, int rt) {
    seg[rt].val = 0;
    seg[rt].vis = true;//初始化为true,说明所有节点的所有子节点全部相同(即全为0)
    if (!(r - l))
        return ;
    int mid = l + (r - l >> 1);
    Create(l, mid, lson);
    Create(mid + 1, r, rson);
}

//向上更新
void PushUp(int rt) {
    if (!seg[lson].vis || !seg[rson].vis)//如果子节点有假,那么该根节点必为假
        seg[rt].vis = false;
    else if (seg[lson].val != seg[rson].val)//如果左右孩子的值不一样,那么这个标记不能做到该根节点上去
        seg[rt].vis = false;
    else {//如果能做到,就将该根节点标记为1,同时为根节点赋值
        seg[rt].vis = true;
        seg[rt].val = seg[lson].val;
    }
}

//向下更新
void PushDown(int rt) {
    if (seg[rt].vis) {
        seg[lson] = seg[rson] = seg[rt];//根节点标记为1说明子节点的值全部一样,为左右孩子赋值.
        seg[rt].vis = false;
    }
}

//更新
void Update(int Ql, int Qr, int Q, int v, int l, int r, int rt) {
    if (Ql <= l && Qr >= r && seg[rt].vis) {//如果该区间在查找区间范围内,并且该根节点标记为1说明他下面的子节点全部值都一样
        if (Q != 1) {
            if (Q != 2)
                seg[rt].val = v;
            else seg[rt].val = seg[rt].val * v % MOD;
        }
        else seg[rt].val = (seg[rt].val + v) % MOD;
        return ;
    }
    PushDown(rt);
    int mid = l + (r - l >> 1);
    if (Ql <= mid)
        Update(Ql, Qr, Q, v, l, mid, lson);
    if(Qr > mid)
        Update(Ql, Qr, Q, v, mid + 1, r, rson);
    PushUp(rt);
}

//查询
int Query(int Ql, int Qr, int Q, int l, int r, int rt) {
    if (Ql <= l && Qr >= r && seg[rt].vis) {
        int ans = 1;
        for (int i = 0; i < Q; i++)
            ans = ans * seg[rt].val % MOD;
        ans = ans * (r - l + 1) % MOD;//区间里有r-l+1个一样的值,所以只要求出来1个再乘以区间长度即可
        return ans;
    }
    int ans = 0;
    PushDown(rt);
    int mid = l + (r - l >> 1);
    if (Ql <= mid)
        ans = (ans + Query(Ql, Qr, Q, l, mid, lson)) % MOD;
    if (Qr > mid)
        ans = (ans + Query(Ql, Qr, Q, mid + 1, r, rson)) % MOD;
    return ans;
}

int main() {
    int n, m;
    while (scanf("%d%d", &n, &m), n + m) {
        Create(1, n, 1);
        while (m--) {
            int op, l, r, x;
            scanf("%d%d%d%d", &op, &l, &r, &x);
            if (op != 4)
                Update(l, r, op, x, 1, n, 1);
            else printf("%d\n", Query(l, r, x, 1, n, 1));
        }
    }
    return 0;
}
展开阅读全文
©️2020 CSDN 皮肤主题: 代码科技 设计师: Amelia_0503 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值