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

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


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清空该区间标记,再进行各种标记,查询的时候是整个算法的精髓,他是查询到要查询区间的子区间的等号标记不为初始标记,即为一段相同的数字的时候停止,然后返回该区间的值,进行各个子区间累加,得出答案。

写法二(2574MS、6516k):

 #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(线段树的多种区间操作)的更多相关文章

  1. hdu 4578 Transformation 线段树多种操作裸题

    自己写了一个带结构体的WA了7.8次 但是测了几组小数据都对..感觉问题应该出在模运算那里.写完这波题解去对拍一下. 以后线段树绝不写struct!一般的struct都带上l,r 但是一条线段的长度确 ...

  2. HDU 4578 Transformation --线段树,好题

    题意: 给一个序列,初始全为0,然后有4种操作: 1. 给区间[L,R]所有值+c 2.给区间[L,R]所有值乘c 3.设置区间[L,R]所有值为c 4.查询[L,R]的p次方和(1<=p< ...

  3. hdu 4578 Transformation 线段树

    没什么说的裸线段树,注意细节就好了!!! 代码如下: #include<iostream> #include<stdio.h> #include<algorithm> ...

  4. Transformation HDU - 4578(线段树——懒惰标记的妙用)

    Yuanfang is puzzled with the question below: There are n integers, a 1, a 2, …, a n. The initial val ...

  5. HDU 3308 LCIS(线段树单点更新区间合并)

    LCIS Given n integers. You have two operations: U A B: replace the Ath number by B. (index counting ...

  6. 约会安排 HDU - 4553(线段树区间查询,区间修改,区间合并)

    题目: 寒假来了,又到了小明和女神们约会的季节.  小明虽为屌丝级码农,但非常活跃,女神们常常在小明网上的大段发言后热情回复“呵呵”,所以,小明的最爱就是和女神们约会.与此同时,也有很多基友找他开黑, ...

  7. uva297 Quadtrees (线段树思想,区间操作)

    借鉴了线段数区间操作的思想,只是把一个结点的孩子扩展到了4个, 结点k,四个孩子编号分别为4*k+1,4*k+2,4*k+3,4*K+4,从零开始. 根据层数,确定权值. #include<cs ...

  8. hdu 4031 attack 线段树区间更新

    Attack Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others)Total Subm ...

  9. HDU.1394 Minimum Inversion Number (线段树 单点更新 区间求和 逆序对)

    HDU.1394 Minimum Inversion Number (线段树 单点更新 区间求和 逆序对) 题意分析 给出n个数的序列,a1,a2,a3--an,ai∈[0,n-1],求环序列中逆序对 ...

随机推荐

  1. maven学习(三)-使用maven来创建项目

    转自https://www.cnblogs.com/xdp-gacl/p/4240930.html maven作为一个高度自动化构建工具,本身提供了构建项目的功能,下面就来体验一下使用maven构建项 ...

  2. CF1217B Zmei Gorynich

    You are fighting with Zmei Gorynich — a ferocious monster from Slavic myths, a huge dragon-like rept ...

  3. cf1200 E Compress Words(哈希)

    题意 有n个字符串,记为s1,s2……sn,s2与s1合并,合并的方式为:s1的后缀若与s2的前缀相同,就可以重叠起来,要最长的. 举个例子: “1333”  “33345” → “133345” s ...

  4. 提升Python编程效率的几种方法

    前言 我们知道Python这门语言在运行速度上已经败给了许多别的语言(比如C, C++, Java, Golang....).但从一个开发者的角度来看Python是我最喜欢的语言,很大一部分原因在于其 ...

  5. 每天一点点之vue框架开发 - 数据渲染-for循环中动态设置页面背景色

    实现方式很简单,在属性前加:,表示绑定 :style="{'background':item.bgColor} 代码样例: <li v-for="item in laber_ ...

  6. POJ 1663:Number Steps

    Number Steps Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 13664   Accepted: 7378 Des ...

  7. lvm 逻辑卷分区删除恢复

    原因:执行 lvremove /dev/system/lv_trans 删除逻辑分区 恢复: 1.进入到lvm查看元数据 cd /etc/lvm/archive 2.恢复元vg卷组 vgcfgrest ...

  8. DLL对应的导入库一定会生成的

    测试代码: #pragma once #define TESTDEPEND_EXPORTS #ifdef TESTDEPEND_EXPORTS #define TESTDEPEND_API __dec ...

  9. nodejs(9)使用arttemplate渲染动态页面

    使用arttemplate渲染动态页面 安装 两个包 npm i art-template express-art-template -S 自定义一个模板引擎 app.engine('自定义模板引擎的 ...

  10. 吴裕雄--天生自然MySQL学习笔记:MySQL 运算符

    要介绍 MySQL 的运算符及运算符的优先级. MySQL 主要有以下几种运算符: 算术运算符 比较运算符 逻辑运算符 位运算符 算术运算符 MySQL 支持的算术运算符包括: 在除法运算和模运算中, ...