codevs 2216 线段树 两种更新方式的冲突
2016-07-31 14:31
211 查看
题目描述 Description
“神州“载人飞船的发射成功让小可可非常激动,他立志长大后要成为一名宇航员假期一始,他就报名参加了“小小宇航员夏令营”,在这里小可可不仅学到了丰富的宇航知识,还参与解决了一些模拟飞行中发现的问题,今天指导老师交给他一个任务,在这次模拟飞行的路线上有N个行星,暂且称它们为一个行星序列,并将他们从1至n标号,在宇宙未知力量的作用下这N个行星的质量是不断变化的,所以他们对飞船产生的引力也会不断变化,小可可的任务就是在飞行途中计算这个行星序列中某段行星的质量和,以便能及时修正飞船的飞行线路,最终到达目的地,行星序列质量变化有两种形式:
1,行星序列中某一段行星的质量全部乘以一个值
2,行星序列中某一段行星的质量全部加上一个值
由于行星的质量和很大,所以求出某段行星的质量和后只要输出这个值模P的结果即可,小可可被这个任务难住了,聪明的你能够帮他完成这个任务吗?
输入描述 Input Description
第一行两个整数N和P(1<=p<=1000000000);
第二行含有N个非负整数,从左到右依次为a1,a2,…………,an(0<=ai<=100000000,1<=i<=n),其中ai表示第i个行星的质量:
第三行有一个整数m,表示模拟行星质量变化以及求质量和等操作的总次数。从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操作1:1 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai*c
操作2:2 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai+c
操作3:3 t g 表示输出所有满足t<=i<=g的ai的和模p的值
其中:1<=t<=g<=N,0<=c<=10000000
注:同一行相邻的两数之间用一个空格隔开,每行开头和末尾没有多余空格
输出描述 Output Description
对每个操作3,按照它在输入中出现的顺序,依次一行输出一个整数表示所求行星质量和
样例输入 Sample Input
样例输出 Sample Output
数据范围及提示 Data Size & Hint
100%的数据中,M,N<=100000
40%的数据中,M,N<=10000
题意:中文题面,对一段序列的三种操作,重点是更新操作有两种
某个区间的数都增加c或者都乘以c
题解: 此题注意开long long
如果是单个更新操作的话,就是很容易的区间延迟标记,但是如果是两种不同的操作呢,这样是会有冲突的,
也就是对于一个下放区间的标记是先处理乘法呢?还是先处理加法呢?
参考了别人的代码之后,我是这样理解的,乘法的标记只对某个区间的sum值起作用,并且对于乘法标记的下放,
不仅要更新下放区间的plu,还要将plu转换为add
也可以这样考虑,就某一个数无非两种标记的执行顺序不同导致结果不同
先加后乘(add+sum)*plu=sum*plu+add*plu;
先乘后加sum*plu+add=sum*plu+add*1;
观察一下如何使得两种情况一起处理呢?就是把add*plu转化为新的add
具体看代码;
“神州“载人飞船的发射成功让小可可非常激动,他立志长大后要成为一名宇航员假期一始,他就报名参加了“小小宇航员夏令营”,在这里小可可不仅学到了丰富的宇航知识,还参与解决了一些模拟飞行中发现的问题,今天指导老师交给他一个任务,在这次模拟飞行的路线上有N个行星,暂且称它们为一个行星序列,并将他们从1至n标号,在宇宙未知力量的作用下这N个行星的质量是不断变化的,所以他们对飞船产生的引力也会不断变化,小可可的任务就是在飞行途中计算这个行星序列中某段行星的质量和,以便能及时修正飞船的飞行线路,最终到达目的地,行星序列质量变化有两种形式:
1,行星序列中某一段行星的质量全部乘以一个值
2,行星序列中某一段行星的质量全部加上一个值
由于行星的质量和很大,所以求出某段行星的质量和后只要输出这个值模P的结果即可,小可可被这个任务难住了,聪明的你能够帮他完成这个任务吗?
输入描述 Input Description
第一行两个整数N和P(1<=p<=1000000000);
第二行含有N个非负整数,从左到右依次为a1,a2,…………,an(0<=ai<=100000000,1<=i<=n),其中ai表示第i个行星的质量:
第三行有一个整数m,表示模拟行星质量变化以及求质量和等操作的总次数。从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操作1:1 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai*c
操作2:2 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai+c
操作3:3 t g 表示输出所有满足t<=i<=g的ai的和模p的值
其中:1<=t<=g<=N,0<=c<=10000000
注:同一行相邻的两数之间用一个空格隔开,每行开头和末尾没有多余空格
输出描述 Output Description
对每个操作3,按照它在输入中出现的顺序,依次一行输出一个整数表示所求行星质量和
样例输入 Sample Input
7 43 1 2 3 4 5 6 7 5 1 2 5 5 3 2 4 2 3 7 9 3 1 3 3 4 7
样例输出 Sample Output
2 35 8
数据范围及提示 Data Size & Hint
100%的数据中,M,N<=100000
40%的数据中,M,N<=10000
题意:中文题面,对一段序列的三种操作,重点是更新操作有两种
某个区间的数都增加c或者都乘以c
题解: 此题注意开long long
如果是单个更新操作的话,就是很容易的区间延迟标记,但是如果是两种不同的操作呢,这样是会有冲突的,
也就是对于一个下放区间的标记是先处理乘法呢?还是先处理加法呢?
参考了别人的代码之后,我是这样理解的,乘法的标记只对某个区间的sum值起作用,并且对于乘法标记的下放,
不仅要更新下放区间的plu,还要将plu转换为add
也可以这样考虑,就某一个数无非两种标记的执行顺序不同导致结果不同
先加后乘(add+sum)*plu=sum*plu+add*plu;
先乘后加sum*plu+add=sum*plu+add*1;
观察一下如何使得两种情况一起处理呢?就是把add*plu转化为新的add
具体看代码;
/****************************** code by drizzle blog: www.cnblogs.com/hsd-/ ^ ^ ^ ^ O O ******************************/ //#include<bits/stdc++.h> #include<iostream> #include<cstring> #include<cstdio> #include<map> #include<algorithm> #include<cmath> #define ll long long #define PI acos(-1.0) #define mod 1000000007 using namespace std; struct node { int l,r; ll sum; ll add; ll plu; } tree[400005]; int n; ll p; int q; int exm; int l1,r1; ll cc; void build(int root,int left,int right) { tree[root].l=left; tree[root].r=right; tree[root].add=0; tree[root].plu=1; if(left==right) { tree[root].sum=0; scanf("%lld",&tree[root].sum); tree[root].sum%=p; return ; } int mid=(left+right)>>1; build(root<<1,left,mid); build(root<<1|1,mid+1,right); tree[root].sum=(tree[root<<1].sum+tree[root<<1|1].sum)%p; } void pushdown(int root) { if(tree[root].l==tree[root].r) return ; ll pl=tree[root].plu;//针对区间sum起作用 ll ad=tree[root].add; tree[root<<1].sum=(tree[root<<1].sum*pl%p+(tree[root<<1].r-tree[root<<1].l+1)*ad%p)%p; tree[root<<1|1].sum=(tree[root<<1|1].sum*pl%p+(tree[root<<1|1].r-tree[root<<1|1].l+1)*ad%p)%p; tree[root<<1].add=(tree[root<<1].add*pl+ad)%p;//对于这个区间的每个元素将plu转化为了add tree[root<<1|1].add=(tree[root<<1|1].add*pl+ad)%p; tree[root<<1].plu=(tree[root<<1].plu*pl)%p; tree[root<<1|1].plu=(tree[root<<1|1].plu*pl)%p; tree[root].add=0; tree[root].plu=1; } void updata(int root,int left,int right,ll c,int flag) { pushdown(root); if(tree[root].l==left&&tree[root].r==right) { if(flag==1) { tree[root].plu=(tree[root].plu*c)%p; tree[root].sum=tree[root].sum*c%p; return ; } if(flag==2) { tree[root].add=(tree[root].add+c)%p; tree[root].sum=(tree[root].sum+c*(right-left+1))%p; return ; } } int mid=(tree[root].l+tree[root].r)>>1; if(right<=mid) updata(root<<1,left,right,c,flag); else { if(left>mid) updata(root<<1|1,left,right,c,flag); else { updata(root<<1,left,mid,c,flag); updata(root<<1|1,mid+1,right,c,flag); } } tree[root].sum=(tree[root<<1].sum+tree[root<<1|1].sum)%p; } ll query(int root,int left,int right) { pushdown(root); if(tree[root].l==left&&tree[root].r==right) { return tree[root].sum; } int mid=(tree[root].l+tree[root].r)>>1; if(right<=mid) return query(root<<1,left,right); else { if(left>mid) return query(root<<1|1,left,right); else return query(root<<1,left,mid)+query(root<<1|1,mid+1,right); } } int main() { while(~scanf("%d %lld",&n,&p)) { build(1,1,n); scanf("%d",&q); for(int i=1; i<=q; i++) { scanf("%d",&exm); if(exm==1) { scanf("%d %d %lld",&l1,&r1,&cc); updata(1,l1,r1,cc,1); } if(exm==2) { scanf("%d %d %lld",&l1,&r1,&cc); updata(1,l1,r1,cc,2); } if(exm==3) { scanf("%d %d",&l1,&r1); printf("%lld\n",query(1,l1,r1)%p); } } } return 0; }
相关文章推荐
- POJ 3225 线段树区间更新(两种更新方式)
- JavaScript实现二级、多级(N级)联动下拉列表框更新版,支持IE6,FireFox,函数和类两种调用方式,支持到N级,非常通用
- HDU 1556 Color the ball 很典型的更新区间查找点的题(线段树树状数组两种解法)
- 关于使用lazytag的线段树两种查询方式的比较研究
- android stuido离线更新的两种方式(亲测可用)
- 线段树的两种查询方式
- 子线程利用handler更新主UI的两种方式
- JavaScript实现二级、多级(N级)联动下拉列表框更新版,支持IE6,FireFox,函数和类两种调用方式,支持到N级,非常通用。
- codevs 1081 线段树练习 2 区间更新 单点查询 无lazy
- Oracle并行更新的两种方式(merge/update内联视图)
- Android通过Handler与AsyncTask两种方式动态更新ListView(附源码)
- HASH表解决冲突的两种方式
- C#中数据库的更新和删除记录的两种实现方式
- notifyDataSetChanged() 动态更新ListView 通过 Handler AsyncTask两种方式
- 使用邮件和RSS两种方式,订阅博客更新通知
- 使用DataSet更新数据库的两种方式
- hdu 1166 敌兵布阵 单点更新 树状数组 线段树 两种做法
- 等值更新的两种方式
- notifyDataSetChanged() 动态更新ListView 通过 Handler AsyncTask两种方式
- Android 更新UI的两种方式--Handler & runOnUiThread