HDU 4578 Transformation (线段树区间多种更新)

http://acm.hdu.edu.cn/showproblem.php?pid=4578

题目大意:对于一个给定序列,序列内所有数的初始值为0,有4种操作。1:区间(x, y)内的所有数字全部加上C;2:区间(x, y)内所有数字全部乘C; 3:区间(x, y)内的所有数字全部重置为C;

4:输出区间(x, y)内所有数字的P次方的和。由于题目为多实例(至少10组样例),故很耿直的更新到叶子节点明显会TLE;因此需优化。可发现题目所有操作都是对区间进行,因此只需要更新

到区间内数字相同即可。再者注意可进行状态压缩,不需要的累加和累乘只标记即可,需要此部分时再往下更新;更新时先更新3,因为3会覆盖掉1和2;之后再进行累乘,因为累乘影响累加,累

加不影响累乘。注意细节即可。

///至少10组样例,则八秒实际上不多,需优化。
///由于所有操作都是对区间进行,故数字保存情况为
///分区间相同,因此只需要操作到区间数字相同时即可,不必处理到最下面的叶子节点
#include <stdio.h>
#include <algorithm>
using namespace std;
#define N 110000
#define mod 10007
#define lson rt<<1
#define rson rt<<1|1
struct tree
{
    int l, r, add, mul, op, num;
    ///add记录累加的数,mul记录累乘的数,op记录操作的状态
    ///op为1表示区间内数字相同,op为0表示区间内数字不同,需向下
    ///继续进行操作,op为2表示区间被重新赋值,需向下更新(操作3)。
    int len()
    {
        return r-l+1;
    }
}a[N<<2];
void build(int l, int r, int rt)///初始化
{
    a[rt].l = l;
    a[rt].r = r;
    a[rt].mul = a[rt].op = 1;
    a[rt].add = a[rt].num = 0;
    if(l==r)return ;
    int mid = (l+r)/2;
    build(l, mid, lson);
    build(mid+1, r, rson);
}
void Change(int rt, int op, int k)
{
    if(op==3)///重新赋值,即再次初始化
    {
        a[rt].num = k%mod;
        a[rt].mul = 1;
        a[rt].add = 0;
        a[rt].op = 2;///重新赋值后子区间所有都重新覆盖
    }
    else if(op==2)
    {
        (a[rt].add *= k) %= mod;
        (a[rt].mul *= k) %= mod;
        (a[rt].num *= k) %= mod;
    }
    else
    {
        (a[rt].add += k) %= mod;
        (a[rt].num += k) %= mod;
    }
}

void Up(int rt)
{
    if(a[lson].op && a[rson].op)///若子区间为同数字区间且两子区间数字相同,
    if(a[lson].num == a[rson].num)///则可向上合并给父区间
    {
        a[rt].num = a[lson].num;
        a[rt].op = 1;
    }
}

void Down(int rt)///向下的状态压缩,若不需此区间作答此区间暂时储存;
{                ///若需此区间作答则向下更新一层直到叶子节点
    if(a[rt].l != a[rt].r)
    {
        if(a[rt].op==2)
        {
            a[lson].num = a[rson].num = a[rt].num;

            a[lson].op = a[rson].op = 2;
            a[lson].add = a[rson].add = 0;
            a[lson].mul = a[rson].mul = 1;

            a[rt].add = 0;
            a[rt].mul = 1;
            a[rt].op = 1;
        }

        if(a[rt].mul>1)///注意此处,先更新乘法,因为累乘会影响累加的状态
        {
            (a[lson].num *= a[rt].mul) %= mod;
            (a[lson].add *= a[rt].mul) %= mod;
            (a[lson].mul *= a[rt].mul) %= mod;

            (a[rson].num *= a[rt].mul) %= mod;
            (a[rson].add *= a[rt].mul) %= mod;
            (a[rson].mul *= a[rt].mul) %= mod;

            a[rt].mul = 1;
        }

        if(a[rt].add)
        {
            (a[lson].num += a[rt].add) %= mod;
            (a[lson].add += a[rt].add) %= mod;

            (a[rson].num += a[rt].add) %= mod;
            (a[rson].add += a[rt].add) %= mod;

            a[rt].add = 0;
        }
    }
}
void Update(int rt, int op, int l, int r, int k)
{
    if(a[rt].l==l && a[rt].r==r && a[rt].op)///找到数字相同区间
    {
        Change(rt, op, k);///执行操作
        return ;
    }

    Down(rt);
    a[rt].op = 0;///假设默认区间数字已改变,标记为不同。

    int mid = (a[rt].l + a[rt].r)/2;
    if(mid>=r)Update(lson, op, l, r, k);
    else if(mid<l)Update(rson, op, l, r, k);
    else
    {
        Update(lson, op, l, mid, k);
        Update(rson, op, mid+1, r, k);
    }

    Up(rt);///执行操作后向上回溯,用已得到的子区间反馈负区间的状态
}
int Query(int rt, int l, int r, int p)
{
    if(a[rt].l==l && a[rt].r==r && a[rt].op)///找到同数字区间即可计算
    {
        int ans = 1;
        for(int i=1; i<=p; i++)///一个p次方
            (ans *= a[rt].num) %= mod;
        ans = (ans * a[rt].len())%mod; ///区间内所有p次方
        return ans;
    }

    Down(rt);

    int mid = (a[rt].l + a[rt].r)/2;

    if(mid>=r)return Query(lson, l, r, p);
    else if(mid<l)return Query(rson, l, r, p);
    else
    {
        int lans = Query(lson, l, mid, p);
        int rans = Query(rson, mid+1, r, p);
        return (lans+rans)%mod;
    }
}
int main()
{
    int n, m;
    while(scanf("%d %d", &n, &m), m+n)
    {
        build(1, n, 1);
        int op, l, r, k;
        while(m--)
        {
            scanf("%d %d %d %d", &op, &l, &r, &k);
            if(op!=4)Update(1, op, l, r, k);///只要不为4都是更新操作
            else printf("%d\n", Query(1, l, r, k));
        }
    }
    return 0;
}
时间: 2024-08-08 05:37:06

HDU 4578 Transformation (线段树区间多种更新)的相关文章

HDU 4578 Transformation --线段树,好题

题意: 给一个序列,初始全为0,然后有4种操作: 1. 给区间[L,R]所有值+c 2.给区间[L,R]所有值乘c 3.设置区间[L,R]所有值为c 4.查询[L,R]的p次方和(1<=p<=3) 解法: 线段树,维护三个标记,addmark,mulmark,setmark分别表示3种更新,然后p[1],p[2],p[3]分别表示该节点的1,2,3次方和.标记传递顺序setmark一定是第一个,因为setmark可以使mulmark,addmark都失效还原,mulmark和addmark的顺

hdu 4578 Transformation(线段树)

Transformation Time Limit: 15000/8000 MS (Java/Others)    Memory Limit: 65535/65536 K (Java/Others)Total Submission(s): 3084    Accepted Submission(s): 749 Problem Description Yuanfang is puzzled with the question below: There are n integers, a1, a2,

HDU 3308 LCIS (线段树区间合并)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目很好懂,就是单点更新,然后求区间的最长上升子序列. 线段树区间合并问题,注意合并的条件是a[mid + 1] > a[mid],写的细心点就好了. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int MAXN = 1

HDU 3308 LCIS 线段树 区间更新

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目描述: 有两种操作, U x y  , 第xth赋值为y .Q x y , 查询区间x-y的最长连续上升子序列的长度L 解题思路: 对于线段树不好的我依然好难.....有太多细节需要注意了....但是这是一道很好的题, 一段区间的L可能从三个地方来, 一种是中间, 一种是以左起点为开头的, 一种是以右起点结尾的, 这样查询的时候就要注意了: 如果两段的中间值是a[m] < a[m+1]

HDU 5316 Magician(线段树区间合并, 子序列最值 多校2015啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5316 Problem Description Fantasy magicians usually gain their ability through one of three usual methods: possessing it as an innate talent, gaining it through study and practice, or receiving it from an

hdu 1540(线段树区间合并)

题目链接:传送门 参考文章:传送门 题意:n个数字初始连在一条线上,有三种操作, D x表示x号被摧毁: R 表示恢复剩下的通路 Q表示查询标号为x所在的串的最长长度. 思路:线段树的区间合并. #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 50500; struct Node{ int l,r; int ls,rs,ms; }cur

HDU 1754(线段树区间最值)

I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 48513    Accepted Submission(s): 18986 Problem Description 非常多学校流行一种比較的习惯.老师们非常喜欢询问.从某某到某某其中,分数最高的是多少. 这让非常多学生非常反感. 无论你喜不喜欢,如今须要你做的是,就是

[HDU] 2795 Billboard [线段树区间求最值]

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11861    Accepted Submission(s): 5223 Problem Description At the entrance to the university, there is a huge rectangular billboard of s

HDU 1540(线段树+区间合并)学习记录

学习了线段树的新姿势,记录一下 参考blog:https://blog.csdn.net/sunyutian1998/article/details/79618316 query的时候m-ql+1和qr-m写成了m-l+1.r-m,wa了几发之后才找到bug 错误样例: 10 1Q 5 wrong answer:5 顺带一提,这个题目可以在set上面二分直接过,不过最主要还是为了练习区间合并 #include<bits/stdc++.h> using namespace std; const