HDU-4578 Transformation(线段树的多种区间操作)
http://acm.hdu.edu.cn/showproblem.php?pid=4578
Time Limit: 15000/8000 MS (Java/Others) Memory Limit: 65535/65536 K (Java/Others)
Problem Description
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
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
Sample Input
Sample Output
Source
题意:
给你一个数组,初始值为零,有四种操作:
(1)"1 x y c",代表 把区间 [x,y] 上的值全部加c
(2)"2 x y c",代表 把区间 [x,y] 上的值全部乘以c
(3)"3 x y c" 代表 把区间 [x,y]上的值全部赋值为c
(4)"4 x y p" 代表 求区间 [x,y] 上值的p次方和1<=p<=3
注意:当线段树有多个懒惰标记时,一定要考虑到懒惰标记之间的互相影响。
解法一:
使用懒惰标记标记的是整个区间是否为同一个状态
并且每次更新的时候回溯状态 即如果左右子结点都是相同状态 则在线段树的父节点上更新标记这一统一状态
如此一来,只需要在计算区间的时候先判断区间内状态是否相同,然后进行区间内的更新操作即可 因为区间内都为统一状态。
写法一(2433MS、5500k):
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <string>
#include <math.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <stack>
#include <map>
#include <math.h>
const int INF=0x3f3f3f3f;
typedef long long LL;
const int mod=1e4+;
const int maxn=1e5+;
using namespace std; struct SegTree_node
{
int l;
int r;
int lazy;//懒惰标记 但是标记的是整个区间是否为同一个状态
int num;
}SegTree[maxn<<]; int n,m; void Build(int l,int r,int rt)
{
SegTree[rt].l=l;
SegTree[rt].r=r;
SegTree[rt].lazy=;
SegTree[rt].num=;
if(l==r)
return ;
int mid=(l+r)>>;
Build(l,mid,rt<<);
Build(mid+,r,rt<<|);
} void PushUp(int rt)
{
if(SegTree[rt<<].lazy&&SegTree[rt<<|].lazy&&SegTree[rt<<].num==SegTree[rt<<|].num)
{//可以下放,因为我们每次都是先递归下去更改的子节点的值,所以在更改后将子节点的值赋给父节点。
SegTree[rt].lazy=;
SegTree[rt].num=SegTree[rt<<].num;
}
else//子节点的懒标记不同时为0或子节点值都不一样,肯定区间值不一样
SegTree[rt].lazy=;
} void PushDown(int rt)//下放懒标记
{
if(SegTree[rt].l==SegTree[rt].r)
return ;
if(SegTree[rt].lazy)
{
SegTree[rt<<].lazy=SegTree[rt<<|].lazy=;
SegTree[rt<<].num=SegTree[rt<<|].num=SegTree[rt].num;
SegTree[rt].lazy=;
}
} void Update(int L,int R,int C,int op,int rt)
{
int l=SegTree[rt].l;
int r=SegTree[rt].r;
if(L<=l&&R>=r&&SegTree[rt].lazy)//这里要注意,一定要保证现在区间值都一样才能修改。
{
if(op==)
SegTree[rt].num=(SegTree[rt].num+C)%mod;
else if(op==)
SegTree[rt].num=(SegTree[rt].num*C)%mod;
else
SegTree[rt].num=C;
return ;
}
PushDown(rt);
int mid=(l+r)>>;
if(L<=mid)
Update(L,R,C,op,rt<<);
if(R>mid)
Update(L,R,C,op,rt<<|);
PushUp(rt);
} int Query(int L,int R,int P,int rt)
{
int l=SegTree[rt].l;
int r=SegTree[rt].r;
if(L<=l&&R>=r&&SegTree[rt].lazy)//要保证区间值一样才满足sum=(r-l+1)*a[rt]^p;这个式子
{
int ans=;
for(int i=;i<=P;i++)
ans=(ans*SegTree[rt].num)%mod;
ans=(ans*(r-l+))%mod;
return ans;
}
PushDown(rt);
int ans=;
int mid=(l+r)>>;
if(L<=mid)
ans+=Query(L,R,P,rt<<);
if(R>mid)
ans+=Query(L,R,P,rt<<|);
return ans%mod;
} int main()
{
while(~scanf("%d %d",&n,&m)&&(n||m))
{
Build(,n,);
for(int i=;i<=m;i++)
{
int op,x,y,c;
scanf("%d %d %d %d",&op,&x,&y,&c);
if(op>=&&op<=)//更新操作
Update(x,y,c,op,);
else if(op==)//查询操作
{
printf("%d\n",Query(x,y,c,));
}
}
}
return ;
}
写法一的改进(2137MS、5496k):
只有在二分子区间时写法不同
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <string>
#include <math.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <stack>
#include <map>
#include <math.h>
const int INF=0x3f3f3f3f;
typedef long long LL;
const int mod=1e4+;
//const int mod=1e9+7;
//const double PI=acos(-1);
const int maxn=1e5+;
using namespace std;
//ios::sync_with_stdio(false);
// cin.tie(NULL); struct SegTree_node
{
int l;
int r;
int lazy;//懒惰标记 但是标记的是整个区间是否为同一个状态
int num;
}SegTree[maxn<<]; int n,m,ans; void Build(int l,int r,int rt)
{
SegTree[rt].l=l;
SegTree[rt].r=r;
SegTree[rt].lazy=;
SegTree[rt].num=;
if(l==r)
return ;
int mid=(l+r)>>;
Build(l,mid,rt<<);
Build(mid+,r,rt<<|);
} void PushUp(int rt)
{
if(SegTree[rt<<].lazy&&SegTree[rt<<|].lazy&&SegTree[rt<<].num==SegTree[rt<<|].num)
{//可以下放,因为我们每次都是先递归下去更改的子节点的值,所以在更改后将子节点的值赋给父节点。
SegTree[rt].lazy=;
SegTree[rt].num=SegTree[rt<<].num;
}
else//子节点的懒标记不同时为0或子节点值都不一样,肯定区间值不一样
SegTree[rt].lazy=;
} void PushDown(int rt)
{
if(SegTree[rt].l==SegTree[rt].r)
return ;
if(SegTree[rt].lazy)
{
SegTree[rt<<].lazy=SegTree[rt<<|].lazy=;
SegTree[rt<<].num=SegTree[rt<<|].num=SegTree[rt].num;
SegTree[rt].lazy=;
}
} void Update(int L,int R,int C,int op,int rt)
{
int l=SegTree[rt].l;
int r=SegTree[rt].r;
if(L==l&&R==r&&SegTree[rt].lazy)//这里要注意,一定要保证现在区间值都一样才能修改。
{
if(op==)
SegTree[rt].num=(SegTree[rt].num+C)%mod;
else if(op==)
SegTree[rt].num=(SegTree[rt].num*C)%mod;
else
SegTree[rt].num=C;
return ;
}
PushDown(rt);
int mid=(l+r)>>;
if(R<=mid)
Update(L,R,C,op,rt<<);
else if(L>mid)
Update(L,R,C,op,rt<<|);
else
{
Update(L,mid,C,op,rt<<);
Update(mid+,R,C,op,rt<<|);
}
PushUp(rt);
} void Query(int L,int R,int P,int rt)
{
int l=SegTree[rt].l;
int r=SegTree[rt].r;
if(L==l&&R==r&&SegTree[rt].lazy)//要保证区间值一样才满足sum=(r-l+1)*a[rt]^p;这个式子
{
int tem=;
for(int i=;i<=P;i++)
tem=(tem*SegTree[rt].num)%mod;
tem=(tem*(r-l+))%mod;
ans=(ans+tem)%mod;
return ;
}
PushDown(rt);
int mid=(l+r)>>;
if(R<=mid)
Query(L,R,P,rt<<);
else if(L>mid)
Query(L,R,P,rt<<|);
else
{
Query(L,mid,P,rt<<);
Query(mid+,R,P,rt<<|);
}
} int main()
{
while(~scanf("%d %d",&n,&m)&&(n||m))
{
Build(,n,);
ans=;
for(int i=;i<=m;i++)
{
int op,x,y,c;
scanf("%d %d %d %d",&op,&x,&y,&c);
if(op>=&&op<=)//更新操作
Update(x,y,c,op,);
else if(op==)//查询操作
{
ans=;
Query(x,y,c,);
printf("%d\n",ans);
}
}
}
return ;
}
解法二:
用线段树维护里面的值都相等的区间 ,那么这个区间的所需答案为(r-l+1)*(tree[v].eq)^q
用add表示加号标记,mul表示乘,eq表示等,无疑在向下更新的时候等号的优先级应该最高,先处理等号标记,并把加号和乘号标记置为0和1(初始值),然后重点来了,在处理乘号标记的时候,如果发现子区间有等号标记,直接修改等号标记,否则先将子区间pushdwon一下清空标记,再进行该子区间标记,加号同理,如果子区间有等号标记,就修改等号标记,否则将子区间pushdown清空标记,再将该子区间标记,所有操作完都要清空当前区间标记,然后修改的时候也差不多,先检查该区间有没有等号标记,有的话直接修改等号标记,否则pushdown清空该区间标记,再进行各种标记,查询的时候是整个算法的精髓,他是查询到要查询区间的子区间的等号标记不为初始标记,即为一段相同的数字的时候停止,然后返回该区间的值,进行各个子区间累加,得出答案。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <string>
#include <math.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <stack>
#include <map>
#include <math.h>
const int INF=0x3f3f3f3f;
typedef long long LL;
const int mod=1e4+;
const int maxn=1e5+;
using namespace std; struct SegTree_node
{
int l;
int r;
int add;//表示加号标记
int mul;//表示乘号标记
int eq; //表示等号标记
}SegTree[maxn<<]; int n,m; void Build(int l,int r,int rt)
{
SegTree[rt].l=l;
SegTree[rt].r=r;
SegTree[rt].add=;
SegTree[rt].mul=;
SegTree[rt].eq=-;
if(l==r)
{
SegTree[rt].eq=;//最底层要赋值为0
return ;
}
int mid=(l+r)>>;
Build(l,mid,rt<<);
Build(mid+,r,rt<<|);
} void PushDown(int rt)
{
int l=SegTree[rt].l;
int r=SegTree[rt].r;
if(l==r)//没有子区间了不用向下更新了
return ;
if(SegTree[rt].eq!=-)//处理等号
{
SegTree[rt<<].eq=SegTree[rt<<|].eq=SegTree[rt].eq;//更新子区间等号标记
SegTree[rt<<].add=SegTree[rt<<|].add=;//还原子区间乘法标记
SegTree[rt<<].mul=SegTree[rt<<|].mul=;//清空子区间加乘标记
SegTree[rt].eq=-;//记得还原
return ;
}
if(SegTree[rt].mul!=)//处理乘号
{
if(SegTree[rt<<].eq!=-)//如果子区间有等号标记,直接修改等号标记
SegTree[rt<<].eq=(SegTree[rt<<].eq*SegTree[rt].mul)%mod;
else//否则清空该子区间标记,进行子区间标记
{
PushDown(rt<<);
SegTree[rt<<].mul=(SegTree[rt<<].mul*SegTree[rt].mul)%mod;
}
if(SegTree[rt<<|].eq!=-)//同理处理右区间
SegTree[rt<<|].eq=(SegTree[rt<<|].eq*SegTree[rt].mul)%mod;
else
{
PushDown(rt<<|);
SegTree[rt<<|].mul=(SegTree[rt<<|].mul*SegTree[rt].mul)%mod;
}
SegTree[rt].mul=;//记得还原
}
if(SegTree[rt].add)//处理加号标记,和上面同理处理
{
if(SegTree[rt<<].eq!=-)
SegTree[rt<<].eq=(SegTree[rt<<].eq+SegTree[rt].add)%mod;
else
{
PushDown(rt<<);
SegTree[rt<<].add=(SegTree[rt<<].add+SegTree[rt].add)%mod;
}
if(SegTree[rt<<|].eq!=-)
SegTree[rt<<|].eq=(SegTree[rt<<|].eq+SegTree[rt].add)%mod;
else
{
PushDown(rt<<|);
SegTree[rt<<|].add=(SegTree[rt<<|].add+SegTree[rt].add)%mod;
}
SegTree[rt].add=;//记得还原
}
} void Update(int L,int R,int C,int op,int rt)
{
int l=SegTree[rt].l;
int r=SegTree[rt].r;
if(L<=l&&R>=r)
{
if(op==)
{
SegTree[rt].add=;
SegTree[rt].mul=;
SegTree[rt].eq=C;
return ;
}
if(SegTree[rt].eq!=-)//如果有等号标记,就直接修改等号标记
{
if(op==)
SegTree[rt].eq=(SegTree[rt].eq+C)%mod;
else if(op==)
SegTree[rt].eq=(SegTree[rt].eq*C)%mod;
}
else
{
PushDown(rt);//否则清空该区间,进行标记
if(op==)
SegTree[rt].add=(SegTree[rt].add+C)%mod;
else if(op==)
SegTree[rt].mul=(SegTree[rt].mul*C)%mod;
}
return ;
}
PushDown(rt);//向下传递状态
int mid=(l+r)>>;
if(L<=mid)
Update(L,R,C,op,rt<<);
if(R>mid)
Update(L,R,C,op,rt<<|);
} int Query(int L,int R,int P,int rt)//查询
{
int l=SegTree[rt].l;
int r=SegTree[rt].r;
if(L<=l&&R>=r&&SegTree[rt].eq!=-)//查到是查询区间的子区间且一段全为相同的数
{
int ans=;
for(int i=;i<=P;i++)
ans=(ans*SegTree[rt].eq)%mod;
return (ans*(r-l+)) %mod;//注意要乘上长度
}
PushDown(rt);
int mid=(l+r)>>;
if(R<=mid)
return Query(L,R,P,rt<<);
else if(L>mid)
return Query(L,R,P,rt<<|);
else
return (Query(L,mid,P,rt<<)+Query(mid+,R,P,rt<<|))%mod;
} int main()
{
while(~scanf("%d %d",&n,&m)&&(n||m))
{
Build(,n,);
for(int i=;i<=m;i++)
{
int op,x,y,c;
scanf("%d %d %d %d",&op,&x,&y,&c);
if(op>=&&op<=)//更新操作
Update(x,y,c,op,);
else if(op==)//查询操作
printf("%d\n",Query(x,y,c,)%mod);
}
}
return ;
}
HDU-4578 Transformation(线段树的多种区间操作)的更多相关文章
- hdu 4578 Transformation 线段树多种操作裸题
自己写了一个带结构体的WA了7.8次 但是测了几组小数据都对..感觉问题应该出在模运算那里.写完这波题解去对拍一下. 以后线段树绝不写struct!一般的struct都带上l,r 但是一条线段的长度确 ...
- HDU 4578 Transformation --线段树,好题
题意: 给一个序列,初始全为0,然后有4种操作: 1. 给区间[L,R]所有值+c 2.给区间[L,R]所有值乘c 3.设置区间[L,R]所有值为c 4.查询[L,R]的p次方和(1<=p< ...
- hdu 4578 Transformation 线段树
没什么说的裸线段树,注意细节就好了!!! 代码如下: #include<iostream> #include<stdio.h> #include<algorithm> ...
- Transformation HDU - 4578(线段树——懒惰标记的妙用)
Yuanfang is puzzled with the question below: There are n integers, a 1, a 2, …, a n. The initial val ...
- HDU 3308 LCIS(线段树单点更新区间合并)
LCIS Given n integers. You have two operations: U A B: replace the Ath number by B. (index counting ...
- 约会安排 HDU - 4553(线段树区间查询,区间修改,区间合并)
题目: 寒假来了,又到了小明和女神们约会的季节. 小明虽为屌丝级码农,但非常活跃,女神们常常在小明网上的大段发言后热情回复“呵呵”,所以,小明的最爱就是和女神们约会.与此同时,也有很多基友找他开黑, ...
- uva297 Quadtrees (线段树思想,区间操作)
借鉴了线段数区间操作的思想,只是把一个结点的孩子扩展到了4个, 结点k,四个孩子编号分别为4*k+1,4*k+2,4*k+3,4*K+4,从零开始. 根据层数,确定权值. #include<cs ...
- hdu 4031 attack 线段树区间更新
Attack Time Limit: 5000/3000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Others)Total Subm ...
- HDU.1394 Minimum Inversion Number (线段树 单点更新 区间求和 逆序对)
HDU.1394 Minimum Inversion Number (线段树 单点更新 区间求和 逆序对) 题意分析 给出n个数的序列,a1,a2,a3--an,ai∈[0,n-1],求环序列中逆序对 ...
随机推荐
- 数组空值empty
Array构造函数只带一个数字参数时(否则是作为填充),该参数会被作为数组的预设长度,而非填充一个元素,因此数组内是空单元 如果一个数组中存在一个空单元,即length的值大于实际单元数,这样的数组称 ...
- 启动mysql遇到问题Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
在mysql的启动过程中有时会遇到下述错误 Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2) 请问mys ...
- 2020/1/30 PHP代码审计之CSRF漏洞
0x00 CSRF漏洞 CSRF(Cross-site request forgery)跨站请求伪造:也被称为"One Click Attack"或者Session Riding, ...
- offsetof宏与container_of宏
offsetof宏与container_of宏1.由结构体指针进而访问各元素的原理(1)通过结构体整体变量来访问其中各个元素,本质上是通过指针方式来访问的,形式上是通过.的方式来访问的(这个时候其实是 ...
- 【每日Scrum】第六天冲刺
一.计划会议内容 数据库仍然有问题,决定先绕过数据库,进行软件内容设计与界面ui美化. 二.任务看板 三.scrum讨论照片 四.产品的状态 无 五.任务燃尽图
- 施魔法(DP)
链接:https://ac.nowcoder.com/acm/contest/3003/H来源:牛客网 题目描述 牛可乐有 n 个元素( 编号 1..n ),第 i 个元素的能量值为 ai. 牛可乐 ...
- Hadoop的伪分布式安装和部署流程
在opt目录创建install software test other四个目录 /opt/installed #安装包/opt/software #软件包/opt/other #其他/opt/test ...
- Linux(CENTOS7) Redis安装
1.下载redis 在disk目录下,输入以下命令进行下载: wget http://download.redis.io/releases/redis-2.8.3.tar.gz 2.解 ...
- Qt5学习笔记(1)-环境配置(win+64bit+VS2013)
Qt5学习笔记(1)-环境配置 工欲善其事必先-不装-所以装软件 久不露面,赶紧打下酱油. 下载 地址:http://download.qt.io/ 这个小网页就可以下载到跟Qt有关的几乎所有大部分东 ...
- Docker部署zookeeper集群和kafka集群,实现互联
本文介绍在单机上通过docker部署zookeeper集群和kafka集群的可操作方案. 0.准备工作 创建zk目录,在该目录下创建生成zookeeper集群和kafka集群的yml文件,以及用于在该 ...