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. Python2 和 Python3 的区别(待完善)

    1.宏观上 python2 :源码不标准,混乱,重复代码太多 python3 :统一 标准,去除重复代码. 2. print python2 :括号可有可无 print(a)  或  print ap ...

  2. Vmvare + Ubuntu 16.04环境搭建 + 相关软件安装配置笔记【深度学习】

    前言 由于学习与工作的需要,加上之前配置好的vmmachines都损坏了,我就重新弄一个ubuntu虚拟机,配置一下环境,给自己留个记录 1.文件 2.配置过程 1.在Vmware中新建虚拟机,自定义 ...

  3. Unit07: 状态管理-Session

    Unit07: 状态管理-Session web package web; import java.io.IOException; import java.io.PrintWriter; import ...

  4. linux 时钟时间,用户CPU时间,系统CPU时间 .

    之前看过几次这几个的概念,但还是老是记不住,干脆就直接写下来,以后方便看~ 所谓的时钟时间又叫做墙上时钟时间,它是进程运行的时钟总量,其值与系统中同时运行的进程数有关,不过一般在讨论时钟时间的时候都是 ...

  5. ACM-世界岛旅行

    [问题描述] 某旅游公司组团去迪拜世界岛旅游.世界岛由n个岛屿组成,岛屿序号为1-n,这些岛屿都直接或间接相连.岛屿之间用桥梁连接.现从1号岛屿开始游览,并约定按如下方式游览: 1) 每游览完一个岛屿 ...

  6. HDOJ5883(欧拉路)

    The Best Path Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Tot ...

  7. 【JS】手机屏幕旋转判断

    function readDeviceOrientation() { if (Math.abs(window.orientation) === 90) { // Landscape alert('横屏 ...

  8. java结构

    package com.hanqi;// 包名,必须在第一行,和namespace类似 //import java.io.*;//引用,和using类似 //import java.lang.*;// ...

  9. 杂项-数学软件:MATLAB

    ylbtech-杂项-数学软件:MATLAB MATLAB是美国MathWorks公司出品的商业数学软件,用于算法开发.数据可视化.数据分析以及数值计算的高级技术计算语言和交互式环境,主要包括MATL ...

  10. Collection集合学习(一)———Set接口与具体实现

    接口Collection: Collection是Java的一个集合框架, 也是一个根接口.JDK中没有提供此接口的任何实现,但是提供了更具体的子接口Set和List接口的实现,所有的Collecti ...