HDU 4578 - Transformation - [加强版线段树]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4578

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

题意:

给出一个序列,有下列操作:

  1. 对区间[x,y]全部加上c
  2. 对区间[x,y]全部乘上c
  3. 将区间[x,y]全部改成c
  4. 查询区间[x,y]的p次方和

题解:

加强版的线段树,需要三个lazy标记,一个add表示加法标记,一个mul表示乘法标记,一个alt表示修改标记,

同时由于p=1,2,3,所以可以有三个val值:sum1表示一次方和,sum2表示平方和,sum3表示立方和,

然后我们要确定三个标记的优先级:alt第一,mul第二,add第三,pushdown的时候要按照这样的顺序pushdown,

同时下压高优先级的标记,会影响到低优先级的标记,这个需要注意,

另外,在接收到父节点传过来的add标记时,更新自身时(update_add成员函数),要注意计算sum3,sum2,sum1的先后顺序,一定是sum3,sum2,sum1,

这三个sum计算的方法如下:

$\begin{array}{l} \left( {a + x} \right)^2 = a^2 + 2ax + x^2 \\ \left( {a_1 + x} \right)^2 + \left( {a_2 + x} \right)^2 + \cdots + \left( {a_n + x} \right)^2 = \left( {a_1 ^2 + \cdots + a_n ^2 } \right) + 2x\left( {a_1 + \cdots + a_n } \right) + nx^2 \\ \left( {a + x} \right)^3 = a^3 + 3a^2 x + 3ax^2 + x^3 \\ \left( {a_1 + x} \right)^3 + \left( {a_2 + x} \right)^3 + \cdots + \left( {a_n + x} \right)^3 = \left( {a_1 ^3 + \cdots + a_n ^3 } \right) + 3x\left( {a_1 ^2 + \cdots + a_n ^2 } \right) + 3x^2 \left( {a_1 + \cdots + a_n } \right) + nx^3 \\ \end{array}$

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100000+10;
const int MOD=10007;

int n,m;

/********************************* Segment Tree - st *********************************/
struct Node
{
    int l,r;
    int sum1,sum2,sum3;
    int add,mul,alt;
    void Update_Alt(int x)
    {
        x%=MOD;
        sum1 = (r-l+1) * x % MOD;
        sum2 = (r-l+1) * x % MOD * x % MOD;
        sum3 = (r-l+1) * x % MOD * x % MOD * x % MOD;
        alt=x;
        add=0;
        mul=1;
    }
    void Update_Mul(int x)
    {
        x%=MOD;
        sum1 = sum1 % MOD * x % MOD;
        sum2 = sum2 % MOD * x % MOD * x % MOD;
        sum3 = sum3 % MOD * x % MOD * x % MOD * x % MOD;
        mul = mul % MOD * x % MOD;
        add = add % MOD * x % MOD;
    }
    void Update_Add(int x)
    {
        x%=MOD;
        sum3 = ( sum3%MOD + 3*x%MOD*sum2%MOD + 3*x%MOD*x%MOD*sum1%MOD + (r-l+1)*x%MOD*x%MOD*x%MOD ) % MOD;
        sum2 = ( sum2%MOD + 2*x%MOD*sum1%MOD + (r-l+1)%MOD*x%MOD*x%MOD ) % MOD;
        sum1 = ( sum1%MOD + (r-l+1)%MOD*x%MOD ) % MOD;
        add=(add%MOD+x)%MOD;
    }
}node[4*maxn];
void Pushdown(int root)
{
    int ls=root*2, rs=root*2+1;
    if(node[root].alt!=0)
    {
        node[ls].Update_Alt(node[root].alt);
        node[rs].Update_Alt(node[root].alt);
        node[root].alt=0;
    }
    if(node[root].mul!=1)
    {
        node[ls].Update_Mul(node[root].mul);
        node[rs].Update_Mul(node[root].mul);
        node[root].mul=1;
    }
    if(node[root].add!=0)
    {
        node[ls].Update_Add(node[root].add);
        node[rs].Update_Add(node[root].add);
        node[root].add=0;
    }
}
void Pushup(int root)
{
    int ls=root*2, rs=root*2+1;
    node[root].sum1=(node[ls].sum1+node[rs].sum1)%MOD;
    node[root].sum2=(node[ls].sum2+node[rs].sum2)%MOD;
    node[root].sum3=(node[ls].sum3+node[rs].sum3)%MOD;
}
void Build(int root,int l,int r) //对区间[l,r]建树
{
    if(l>r) return;
    node[root].l=l; node[root].r=r;
    node[root].sum1=0;
    node[root].sum2=0;
    node[root].sum3=0;
    node[root].alt=0;
    node[root].add=0;
    node[root].mul=1;

    if(l<r)
    {
        int mid=l+(r-l)/2;
        Build(root*2,l,mid);
        Build(root*2+1,mid+1,r);
        Pushup(root);
    }
}
void Alt(int root,int st,int ed,ll val) //区间[st,ed]全部改成val
{
    if(st>node[root].r || ed<node[root].l) return;
    if(st<=node[root].l && node[root].r<=ed) node[root].Update_Alt(val);
    else
    {
        Pushdown(root);
        Alt(root*2,st,ed,val);
        Alt(root*2+1,st,ed,val);
        Pushup(root);
    }
}
void Mul(int root,int st,int ed,ll val) //区间[st,ed]全部加上val
{
    if(st>node[root].r || ed<node[root].l) return;
    if(st<=node[root].l && node[root].r<=ed) node[root].Update_Mul(val);
    else
    {
        Pushdown(root);
        Mul(root*2,st,ed,val);
        Mul(root*2+1,st,ed,val);
        Pushup(root);
    }
}
void Add(int root,int st,int ed,ll val) //区间[st,ed]全部加上val
{
    if(st>node[root].r || ed<node[root].l) return;
    if(st<=node[root].l && node[root].r<=ed) node[root].Update_Add(val);
    else
    {
        Pushdown(root);
        Add(root*2,st,ed,val);
        Add(root*2+1,st,ed,val);
        Pushup(root);
    }
}
int Query(int root,int st,int ed,int p) //查询区间[st,ed]的p次方和
{
    if(st>node[root].r || ed<node[root].l) return 0;
    if(st<=node[root].l && node[root].r<=ed)
    {
        if(p==1) return node[root].sum1;
        if(p==2) return node[root].sum2;
        if(p==3) return node[root].sum3;
    }
    else
    {
        Pushdown(root);
        int ls=Query(root*2,st,ed,p)%MOD;
        int rs=Query(root*2+1,st,ed,p)%MOD;
        Pushup(root);
        return (ls+rs)%MOD;
    }
}
/********************************* Segment Tree - st *********************************/

int main()
{
    while(scanf("%d%d",&n,&m) && n*m!=0)
    {
        Build(1,1,n);
        for(int i=1;i<=m;i++)
        {
            int op; scanf("%d",&op);
            if(op==1)
            {
                int x,y,k;
                scanf("%d%d%d",&x,&y,&k);
                Add(1,x,y,k);
            }
            if(op==2)
            {
                int x,y,k;
                scanf("%d%d%d",&x,&y,&k);
                Mul(1,x,y,k);
            }
            if(op==3)
            {
                int x,y,k;
                scanf("%d%d%d",&x,&y,&k);
                Alt(1,x,y,k);
            }
            if(op==4)
            {
                int l,r,p;
                scanf("%d%d%d",&l,&r,&p);
                printf("%d\n",Query(1,l,r,p));
            }
        }
    }
}

原文地址:https://www.cnblogs.com/dilthey/p/9459510.html

时间: 2024-10-15 01:07:14

HDU 4578 - Transformation - [加强版线段树]的相关文章

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:因此需优化.可发现题目所有操作都是对区间进行,因此只需要更新 到区间内数字相同即可.

HDU 4902 Nice boat(线段树)

HDU Nice boat 题目链接 题意:给定一个序列,两种操作,把一段变成x,把一段每个数字,如果他大于x,就变成他和x的gcd,求变换完后,最后的序列. 思路:线段树,每个结点多一个cover表示该位置以下区间是否数字全相同,然后每次延迟操作,最后输出的时候单点查询即可 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 1

hdu 2795 Billboard(线段树)

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

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

Transformation(线段树多个lz标记)

这里以3次方来举例讲一下这题的做法,其它维类似. 如果要求某一个值的3次方那么sum = t^3,设t = x+y.那也就是sum = (x+y)^3. 假如我让每个数都加z t = x+y+z,我可以让新的y = y+z,这里发现新来的总会加在y上,那么可以给他一个延迟,slz, 那么新的值t = t + slz,再看如果让每个数都乘k,t = (x+y)*k = xk+yk.可以看出刚才的slz = slz*k; 另外发现系数会跟着变换,可以给他一个延迟,mlz. 那么一个数t = (mlz

HDU 3954 Level up 线段树

---NotOnlySuccess 出的题--- 看了题之后觉得和HDU 4027有点像,给的K很小,只有10,目测只要有人升级的时候直接更新到叶子节点就ok了.不过不同于HDU 4027 的是,那题每一次更新都相当于这题的一次升级操作,这题里面可能会出现一次操作之后没有升级和出现升级两种情况,一时半会没了思路. 无奈去搜题解,发现我只要维护一个区间当中距离升级最近的人所需要的基础升级经验,即不算等级加成的裸的升级经验,如果在一次涨经验之后,出现当前区间当中有人会升级,直接将每一个要升级的人更新

hdu 4578 Transformation

http://acm.hdu.edu.cn/showproblem.php?pid=4578 又做了一道好题.. 有三种操作: 1 a b c [a,b]加上c 2 a b c [a,b]乘上c 3 a b c [a,b]变为c 4 a b c 求[a,b]的c次方和(1<=c<=3) 这题首先需要解决的第一个问题是加上或乘上一个数对这个区间的c次方和分别产生什么改变,很简单,一化简就能得到. 第二个问题是当一段区间上既有乘又有加的lazy时应该怎么向下推送,因为一段区间上只能有一个lazy,

多校训练hdu --Nice boat(线段树,都是泪)

Nice boat Time Limit: 30000/15000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 47 Accepted Submission(s): 10 Problem Description There is an old country and the king fell in love with a devil. The devil always ask

HDU 3308 LCIS(线段树)

Problem Description Given n integers.You have two operations:U A B: replace the Ath number by B. (index counting from 0)Q A B: output the length of the longest consecutive increasing subsequence (LCIS) in [a, b]. Input T in the first line, indicating