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. 学习 Git 玩转 GitHub

    原文地址:学习 Git 玩转 GitHub 博客地址:http://www.extlight.com 一.基本了解 1.1 什么是版本控制系统 版本控制系统是一种记录一个或若干个文件内容变化,以便将来 ...

  2. Unit02: JSON 、 使用JSON实现数据交换 、 jQuery对AJAX的支持,编码问题

    Unit02: JSON . 使用JSON实现数据交换 . jQuery对AJAX的支持 1. 编码问题 (1)发送get请求 为什么会产生乱码? ie浏览器提供的ajax对象,对中文会使用gbk来编 ...

  3. rapidjson的read和write的sample

    头文件 #include "json/document.h" #include "json/prettywriter.h" #include "jso ...

  4. mstsc遇到CredSSP加密Oracle修正

    Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\P ...

  5. python 网络编程--socket模块/struct模块

    socket模块: 客户端:CS架构,  client -> server 浏览器:BS架构,  browser -> server 网络通信本质:传输字节 doc命令查看ip地址:ipc ...

  6. eclipse安装freemarker插件【转】

    今天在Eclipse上安装Freemarker的插件,一开始装官方网站上的推荐插件,装上后发现除了Freemarker代码高亮显示其他什么效果都没有,郁闷.在javaeye论坛上请教了下,据说官网上的 ...

  7. 第三章 k8s cluster环境创建

    1  用如下方法安装指定版本的docker,但是我的环境会报错 # 安装rpm apt install rpm # 下载 RPM 包, docker 版本 wget https://download. ...

  8. Linux: su sudo sudoer

    日常操作中为了避免一些误操作,更加安全的管理系统,通常使用的用户身份都为普通用户,而非root.当需要执行一些管理员命令操作时,再切换成root用户身份去执行. 普通用户切换到root用户的方式有:s ...

  9. IDA Pro 权威指南学习笔记(十一) - 名称与命名

    多数情况下,要修改一个名称,只需单击想要修改的名称(使其突出显示),并使用快捷键 N 打开更名对话框 右击需要修改的名称,并在出现的上下文菜单中选择 Rename 选项,也可以更改名称 参数和局部变量 ...

  10. bower的安装和使用

    第一 下载node 网址https://nodejs.org/en/ 安装过程基本直接“NEXT”就可以了. 安装完成之后,我们先检测下NodeJS是否安装成功,cmd命令行中键入: node -v ...