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

            Transformation

Time Limit: 15000/8000 MS (Java/Others)    Memory Limit: 65535/65536 K (Java/Others)
Total Submission(s): 7556    Accepted Submission(s): 1918

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
 
Source
 

思路:看题意可以很明显地看出是一道线段树题,也是一道很典型的线段树

题意相当明显,就不多说了,有三种操作,这三种操作顺序不一样的话得到的结果是不一样的,对于这种多操作的问题,我们要做的事尽量将多种操作合并成一种操作。

我们可将区间中的数看成是 ax+b的形式,对于加c操作,则变成 ax+b+c(b->b+c),对于乘c操作,则变成 acx+bc,(a->ac,b->bc)对于赋值c操作,则变成c,即(a->1,x->c,b->0),则我们可以在线段数中加以下标记,a,b,x分别是以上提到的变量,sum[3],表示答案,sum[0],sum[1],sum[2]分别表示1次方和,平方和,立方和。对于更新和查询操作我们用pushdown函数向下更新。对于维护平方和还有立方和的问题,我们只要将平方,立方展开再利用更新之前的值即可维护,具体方法这里不多说了

需要说明的是:每次取修改值,如果发现当前遍历区间完全在需要更新的区间内时,更新当前顶点,同时记录本次以及以前的修改操作,此时不再往后跟新。

后面pushdown时就是从这个节点读取修改信息更新后面的点。

关键代码处给出了推到公式:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#define maxn 100010
#define mod 10007
#define mid ((t[p].l+t[p].r)>>1)
#define ls (p<<1)
#define rs (ls|1)
using namespace std;
struct tree
{
int l,r;
int a,b,c; //a * x + b: a为乘法系数, b为需要加的值 ; c 放重置的值
int sum[3]; //三个次方的求和
}t[maxn<<2];
void pushup(int p)
{
int i;
for(i=0;i<3;i++)
t[p].sum[i]=(t[ls].sum[i]+t[rs].sum[i])%mod;
}
void pushdown(int p)
{
if(t[p].l==t[p].r)
return;
int a=t[p].a,b=t[p].b,c=t[p].c;
if(c) //需要重置
{
t[ls].a=t[rs].a=a;
t[ls].b=t[rs].b=b;
t[ls].c=t[rs].c=c;
//sum2 = k * (a * c + b)^2 //重置后每个数相同
t[ls].sum[2]=(t[ls].r-t[ls].l+1)*(a*c%mod+b)%mod*(a*c%mod+b)%mod*(a*c%mod+b)%mod;
t[ls].sum[1]=(t[ls].r-t[ls].l+1)*(a*c%mod+b)%mod*(a*c%mod+b)%mod;
t[ls].sum[0]=(t[ls].r-t[ls].l+1)*(a*c%mod+b)%mod;
t[rs].sum[2]=(t[rs].r-t[rs].l+1)*(a*c%mod+b)%mod*(a*c%mod+b)%mod*(a*c%mod+b)%mod;
t[rs].sum[1]=(t[rs].r-t[rs].l+1)*(a*c%mod+b)%mod*(a*c%mod+b)%mod;
t[rs].sum[0]=(t[rs].r-t[rs].l+1)*(a*c%mod+b)%mod;
}
else //未重置,向下更新求和
{
t[ls].a=(t[ls].a*a)%mod;
t[ls].b=(t[ls].b*a+b)%mod; //先算乘法,再算加法,即:b = b * a + b';
t[rs].a=(t[rs].a*a)%mod;
t[rs].b=(t[rs].b*a+b)%mod;
//(av + b)^3 = a^3*v^3 + b^3 + 3a^2 * v^2 * b + 3av * b^2 //k为区间长度
// 求和为= a^3*sum2 + K * b^3 + 3a^2 * b * sum1 + 3a*b^2 * sum0
t[ls].sum[2]=(a*a%mod*a%mod*t[ls].sum[2]%mod+3*a%mod*a%mod*b%mod*t[ls].sum[1]%mod+3*a%mod*b%mod*b%mod*t[ls].sum[0]%mod+b*b%mod*b%mod*(t[ls].r-t[ls].l+1)%mod)%mod;
//(av + b)^2=a^2*v^2 + b^2 + 2*a*b*v 求和:a^2*sum1 + b^2*k + 2ab*sum1
t[ls].sum[1]=(a*a%mod*t[ls].sum[1]%mod+b*b%mod*(t[ls].r-t[ls].l+1)%mod+2*a*b%mod*t[ls].sum[0]%mod)%mod;
//av + b 求和:a*sum0 + b*k
t[ls].sum[0]=(a*t[ls].sum[0]%mod+b*(t[ls].r-t[ls].l+1)%mod)%mod; t[rs].sum[2]=(a*a%mod*a%mod*t[rs].sum[2]%mod+3*a%mod*a%mod*b%mod*t[rs].sum[1]%mod+3*a%mod*b%mod*b%mod*t[rs].sum[0]%mod+b*b%mod*b%mod*(t[rs].r-t[rs].l+1)%mod)%mod;
t[rs].sum[1]=(a*a%mod*t[rs].sum[1]%mod+b*b%mod*(t[rs].r-t[rs].l+1)%mod+2*a*b%mod*t[rs].sum[0]%mod)%mod;
t[rs].sum[0]=(a*t[rs].sum[0]%mod+b*(t[rs].r-t[rs].l+1)%mod)%mod;
}
t[p].b=t[p].c=0;
t[p].a=1;
}
void build(int p,int l,int r)
{
t[p].l=l,t[p].r=r;
t[p].a=1,t[p].b=t[p].c=0;
t[p].sum[0]=t[p].sum[1]=t[p].sum[2]=0; //全部初始化为0
if(l<r)
{
build(ls,l,mid);
build(rs,mid+1,r);
}
}
void change(int p,int l,int r,int val,int flag)
{
if(t[p].l==l&&t[p].r==r)
{
if(flag==0)//加val
{
t[p].b=(t[p].b+val)%mod;
//y = (x + v)^3 //x为原来的值
//y = x^3 + v^3 + 3 * x^2 * v + 3 * x * v^2;求和 = sum2 + v^3 + 3*sum1*v + 3*sum0*v^2
t[p].sum[2]=(t[p].sum[2]+3*val%mod*t[p].sum[1]%mod+3*val%mod*val%mod*t[p].sum[0]%mod+val*val%mod*val%mod*(t[p].r-t[p].l+1)%mod)%mod;
t[p].sum[1]=(t[p].sum[1]+val*val%mod*(t[p].r-t[p].l+1)%mod+2*val*t[p].sum[0]%mod)%mod;
t[p].sum[0]=(t[p].sum[0]+val*(t[p].r-t[p].l+1))%mod;
}
else if(flag==1) //乘以val
{
t[p].a=(t[p].a*val)%mod;
t[p].b=(t[p].b*val)%mod;
//y = (x * v)^3 = x^3 * v^3;求和 = v^3 * sum2
t[p].sum[2]=val*val%mod*val%mod*t[p].sum[2]%mod;
t[p].sum[1]=val*val%mod*t[p].sum[1]%mod;
t[p].sum[0]=val*t[p].sum[0]%mod;
}
else if(flag==2) //重置为val
{
t[p].a=1;
t[p].b=0;
t[p].c=val;
//y = (v)^3
t[p].sum[2]=(t[p].r-t[p].l+1)%mod*val%mod*val%mod*val%mod;
t[p].sum[1]=(t[p].r-t[p].l+1)%mod*val%mod*val%mod;
t[p].sum[0]=(t[p].r-t[p].l+1)*val%mod;
}
return;
}
pushdown(p);
if(l>mid)
change(rs,l,r,val,flag);
else if(r<=mid)
change(ls,l,r,val,flag);
else
{
change(ls,l,mid,val,flag);
change(rs,mid+1,r,val,flag);
}
pushup(p);
}
int query(int p,int l,int r,int flag)
{
if(t[p].l==l&&t[p].r==r)
return t[p].sum[flag];
pushdown(p);
if(l>mid)
return query(rs,l,r,flag);
else if(r<=mid)
return query(ls,l,r,flag);
else
{
return (query(ls,l,mid,flag)+query(rs,mid+1,r,flag))%mod;
}
}
int main()
{
// freopen("dd.txt","r",stdin);
int n,m;
while(scanf("%d%d",&n,&m)&&(n+m))
{
build(1,1,n);
int q,x,y,c;
while(m--)
{
scanf("%d%d%d%d",&q,&x,&y,&c);
if(q==4)
{
printf("%d\n",query(1,x,y,c-1));
}
else
{
change(1,x,y,c,q-1);
}
}
}
return 0;
}

  

30-Transformation(HDU4578)-区间线段树(复杂)的更多相关文章

  1. hdu 1540 Tunnel Warfare (区间线段树(模板))

    http://acm.hdu.edu.cn/showproblem.php?pid=1540 Tunnel Warfare Time Limit: 4000/2000 MS (Java/Others) ...

  2. BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

    题目大意:有一些位置.这些位置上能够放若干个数字. 如今有两种操作. 1.在区间l到r上加入一个数字x 2.求出l到r上的第k大的数字是什么 思路:这样的题一看就是树套树,关键是怎么套,怎么写.(话说 ...

  3. 【bzoj3110】[Zjoi2013]K大数查询 权值线段树套区间线段树

    题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数 ...

  4. 【BZOJ-4653】区间 线段树 + 排序 + 离散化

    4653: [Noi2016]区间 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 107  Solved: 70[Submit][Status][Di ...

  5. UOJ222 NOI2016 区间 线段树+FIFO队列

    首先将区间按长度排序后离散化端点(这里的“长度”指的是离散化之前区间的实际长度) 然后模拟一个队列,区间按排好的顺序依次进入,直到某个点被覆盖了M次.之后依次出队,直到所有点都被覆盖小于M次 修改和询 ...

  6. 【BZOJ4653】【NOI2016】区间 线段树

    题目大意 数轴上有\(n\)个闭区间\([l_1,r_1],[l_2,r_2],\ldots,[l_n,r_n]\),你要选出\(m\)个区间,使得存在一个\(x\),对于每个选出的区间\([l_i, ...

  7. L3-2 森森快递 (30 分)(贪心+线段树/分块)

    题目链接:https://pintia.cn/problem-sets/1108203702759940096/problems/1108204121661857798 题目大意: 森森开了一家快递公 ...

  8. BZOJ.4653.[NOI2016]区间(线段树)

    BZOJ4653 UOJ222 考虑二分.那么我们可以按区间长度从小到大枚举每个区间,对每个区间可以得到一个可用区间长度范围. 我们要求是否存在一个点被这些区间覆盖至少\(m\)次.这可以用线段树区间 ...

  9. 2018.09.30 bzoj4025: 二分图(线段树分治+并查集)

    传送门 线段树分治好题. 这道题实际上有很多不同的做法: cdq分治. lct. - 而我学习了dzyo的线段树分治+并查集写法. 所谓线段树分治就是先把操作分成lognlognlogn个连续不相交的 ...

随机推荐

  1. string源码分析 ——转载 http://blogs.360.cn/360cloud/2012/11/26/linux-gcc-stl-string-in-depth/

    1. 问题提出 最近在我们的项目当中,出现了两次与使用string相关的问题. 1.1. 问题1:新代码引入的Bug 前一段时间有一个老项目来一个新需求,我们新增了一些代码逻辑来处理这个新需求.测试阶 ...

  2. php分页类 可直接调用

    <?php /** * 分页类 * @author xyy * 调用分页实例 $subPages=new SubPages(数据总条数);//实例化分页类 * //$subPages->s ...

  3. JVM内存管理之GC简介

    为何要了解GC策略与原理?        原因在上一章其实已经有所触及,就是因为在平时的工作和研究当中,不可避免的会遇到内存溢出与内存泄露的问题.如果对GC策略与原理不了解的情况下碰到了前面所说的问题 ...

  4. JVM内存管理之杂谈(借此也论一论obj=null)

    作为一个程序猿,修炼的过程就犹如玄幻小说中的主角,不仅需要练习各种武技,内气的修炼的一样重要.虽然武技可以迅速的提升主角的实力,但是在内气太差的情况下,根本发挥不出武技的十之一二. 因此,在介绍过设计 ...

  5. 今天使用VS2012遇到一个问题:"链接器工具错误 LNK2026 XXX模块对于 SAFESEH 映像是不安全的"

    今天使用VS2012遇到一个问题:"链接器工具错误 LNK2026 XXX模块对于 SAFESEH 映像是不安全的"   解决方法: 1.打开该项目的“属性页”对话框. 2.单击“ ...

  6. 解决jenkins产生的日志过大以及一些衍生问题

          jenkins使用一段时间后,会导致出现比较大的日志问题,经常占满硬盘空间(因为我们使用的硬盘大小20G,无额外存储要求).在硬盘空间占满之后,会导致一些基本的命令都无法使用,譬如tab都 ...

  7. oracle 中GROUP BY的用法

    转自:http://blog.csdn.net/basenet855x/article/details/6694150 问题: select item.itemnum,item.in1,item.in ...

  8. 【UVA】10763 Foreign Exchange(map)

    题目 题目     分析 没什么好说的,字符串拼接一下再放进map.其实可以直接开俩数组排序后对比一下,但是我还是想熟悉熟悉map用法. 呃400ms,有点慢.     代码 #include < ...

  9. 如何实现查询显示N个工作日有效期内的数据

    功能点分析:要显示N个工作日有效期内的数据,需要考虑: 1. 可以给每条数据增加一个有效期时间字段,查询时只显示有效期之前的数据,如有效期为七天,数据的创建时间是2014-07-21,那七个工作日有效 ...

  10. 线程之 CPthon中的GIL与Lock的分析与解决办法

    Cpython 中的GIL锁介绍 1. 前戏 In CPython, the global interpreter lock, or GIL, is a mutex that prevents mul ...