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],求环序列中逆序对 ...
随机推荐
- maven学习(三)-使用maven来创建项目
转自https://www.cnblogs.com/xdp-gacl/p/4240930.html maven作为一个高度自动化构建工具,本身提供了构建项目的功能,下面就来体验一下使用maven构建项 ...
- CF1217B Zmei Gorynich
You are fighting with Zmei Gorynich — a ferocious monster from Slavic myths, a huge dragon-like rept ...
- cf1200 E Compress Words(哈希)
题意 有n个字符串,记为s1,s2……sn,s2与s1合并,合并的方式为:s1的后缀若与s2的前缀相同,就可以重叠起来,要最长的. 举个例子: “1333” “33345” → “133345” s ...
- 提升Python编程效率的几种方法
前言 我们知道Python这门语言在运行速度上已经败给了许多别的语言(比如C, C++, Java, Golang....).但从一个开发者的角度来看Python是我最喜欢的语言,很大一部分原因在于其 ...
- 每天一点点之vue框架开发 - 数据渲染-for循环中动态设置页面背景色
实现方式很简单,在属性前加:,表示绑定 :style="{'background':item.bgColor} 代码样例: <li v-for="item in laber_ ...
- POJ 1663:Number Steps
Number Steps Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 13664 Accepted: 7378 Des ...
- lvm 逻辑卷分区删除恢复
原因:执行 lvremove /dev/system/lv_trans 删除逻辑分区 恢复: 1.进入到lvm查看元数据 cd /etc/lvm/archive 2.恢复元vg卷组 vgcfgrest ...
- DLL对应的导入库一定会生成的
测试代码: #pragma once #define TESTDEPEND_EXPORTS #ifdef TESTDEPEND_EXPORTS #define TESTDEPEND_API __dec ...
- nodejs(9)使用arttemplate渲染动态页面
使用arttemplate渲染动态页面 安装 两个包 npm i art-template express-art-template -S 自定义一个模板引擎 app.engine('自定义模板引擎的 ...
- 吴裕雄--天生自然MySQL学习笔记:MySQL 运算符
要介绍 MySQL 的运算符及运算符的优先级. MySQL 主要有以下几种运算符: 算术运算符 比较运算符 逻辑运算符 位运算符 算术运算符 MySQL 支持的算术运算符包括: 在除法运算和模运算中, ...