洛谷3373 线段树模板
题目详情:https://www.luogu.org/problemnew/show/P3373
这个线段树模板写的头疼(最后纠错发现一个long long没开差点一口血喷出来),思路就是在普通的求区间和线段树中使用两个update函数,一个负责加法,一个负责乘法(同理在线段树的结构体里自然也有addmark和mulmark)。这里我们将延迟标记乘的优先级设置高于延迟标记加,原因是当我需要给一段区间乘一个数,他并不不会因为之后这段区间上加了某一个数而使这个标记改变,但是我先加一个数后再乘一个数,根据mulmark的值,addmark就会有改变,具体看例子。
一个长度为5的数列
1 2 3 4 5
第一次我将它在区间[1,3]上乘以2,他在这段区间上的和直接先乘以2,[1,3]上的延迟标记乘变为2,而我再在区间[1,3]上加2,他除了在这段区间上的和加上了(3-1+1)*2,延迟标记加也变为2,然后我查询[1,2]的和,延迟标记往下传递,他的和就等于(2-1+1)*2+3*2=10(即延迟标记加乘以区间长度加上原值乘以延迟标记乘)。那我们将这个操作反过来,先在区间[1,3]上加2,再乘2,那么首先延迟标记加就会变成2,而在乘2的时候,除了延迟标记乘会变成2,延迟标记加也会变成2*2(延迟标记加*延迟标记乘),所以和为(2-1+1)*4+3*2=14。这是因为之后乘的那个数使之前的增加量也乘了相应的量,所以这样处理。
#include<stdio.h>
#include<stdlib.h>
long long a[5000001]={0};
int n,m,p;
struct seg{long long val;long long mulmark;long long addmark;
}seg[5000000];
int build(int root,int istart,int iend)
{int mid;seg[root].addmark=0;seg[root].mulmark=1;if(istart==iend){seg[root].val=a[istart];}else{mid=(istart+iend)/2;build(root*2,istart,mid);build(root*2+1,mid+1,iend);seg[root].val=seg[root*2].val+seg[root*2+1].val;}seg[root].val=seg[root].val%=p;return 0;
}
long long quire(int root,int nstart,int nend,int qstart,int qend)
{int mid;if(nstart>qend||qstart>nend)return 0;if(qstart<=nstart&&qend>=nend)return seg[root].val;push(root,nstart,nend);mid=(nstart+nend)/2;return (quire(root*2,nstart,mid,qstart,qend)+quire(root*2+1,mid+1,nend,qstart,qend))%p;
}
int push(int root,int nstart,int nend)
{int mid;long long t1,t2;mid=(nstart+nend)/2;t1=seg[root].addmark;t2=seg[root].mulmark;seg[root*2].val=(seg[root*2].val*t2+t1*(mid-nstart+1))%p;seg[root*2+1].val=(seg[root*2+1].val*t2+t1*(nend-mid))%p;seg[root*2].addmark=(seg[root*2].addmark*t2+t1)%p;seg[root*2+1].addmark=(seg[root*2+1].addmark*t2+t1)%p;seg[root*2].mulmark*=t2;seg[root*2].mulmark%=p;seg[root*2+1].mulmark*=t2;seg[root*2+1].mulmark%=p;seg[root].addmark=0;seg[root].mulmark=1;return 0;
}
int updateadd(int root,int nstart,int nend,int ustart,int uend,long long add)
{int mid;if(ustart>nend||nstart>uend)return 0;if(ustart<=nstart&&uend>=nend){seg[root].val+=(nend-nstart+1)*add;seg[root].val%=p;seg[root].addmark+=add;seg[root].addmark%=p;return 0;}push(root,nstart,nend);mid=(nstart+nend)/2;updateadd(root*2,nstart,mid,ustart,uend,add);updateadd(root*2+1,mid+1,nend,ustart,uend,add);seg[root].val=(seg[root*2].val+seg[root*2+1].val)%p;return 0;
}
int updatemul(int root,int nstart,int nend,int ustart,int uend,long long mul)
{int mid;if(ustart>nend||nstart>uend)return 0;if(ustart<=nstart&&uend>=nend){seg[root].val*=mul;seg[root].val%=p;seg[root].mulmark*=mul;seg[root].mulmark%=p;seg[root].addmark*=mul;seg[root].addmark%=p;return 0;}push(root,nstart,nend);mid=(nstart+nend)/2;updatemul(root*2,nstart,mid,ustart,uend,mul);updatemul(root*2+1,mid+1,nend,ustart,uend,mul);seg[root].val=(seg[root*2].val+seg[root*2+1].val)%p;return 0;
}
int main()
{int i,flag,x,y;long long k;scanf("%d%d%d",&n,&m,&p);for(i=1;i<=n;i++)scanf("%lld",&a[i]);build(1,1,n);for(i=1;i<=m;i++){scanf("%d",&flag);if(flag==1){scanf("%d%d%lld",&x,&y,&k);updatemul(1,1,n,x,y,k);}else if(flag==2){scanf("%d%d%lld",&x,&y,&k);updateadd(1,1,n,x,y,k);}else if(flag==3){scanf("%d%d",&x,&y);printf("%lld\n",quire(1,1,n,x,y));}}return 0;
}
洛谷3373 线段树模板相关推荐
- 洛谷 3373 线段树
传送门 思路: 关键在于乘与加的先后计算关系,(x + y) * k = x * k + y * k,从这里可以看出来,把加法转化为乘法计算,取消了+与*先后顺序 pushdown时,即为乘法标记 * ...
- 洛谷 P3373 线段树模板题
链接:https://www.luogu.com.cn/problem/P3373 题意:一个区间 三种操作 1 给lr范围内乘一个数 2 给lr范围内加一个数 3 询问lr范围内的和 啊这题真·做了 ...
- 洛谷P3373线段树
洛谷P3373 线段树模板题,主要对懒标的处理要求比较高. 有三种操作: 区间加法 区间乘法 区间求和查询 tips:我们对一个区间进行乘k操作的时候,他之前可能存在加法lazy还没pushdown, ...
- 洛谷 P3373 线段树2
洛谷 P3373 线段树2 mul和pls更新某区间左右子树sum的时候,别忘了回头更新这个区间的sum 只有在传递给子序列之后,父序列的lz标记才能清零.其他时候,lz标记只增不减 #include ...
- 洛谷P3373线段树2
题目描述 区间查询区间修改,非常明显的线段树模板,但乘法和加法的结合,使问题有了些小改动: problem: 该题唯一的难点就是加法和乘法的lazytag的处理,设目前区间N.s(即区间和)=x,若先 ...
- 洛谷P3373 线段树2(乘法加法lazytag)
线段树模板题,含lazytag的线段树码量本身就比较大,再加入乘法标记,还要考虑先乘后加的问题,本蒟蒻一调就是几个小时. P3373 [模板]线段树 2https://www.luogu.com.cn ...
- [WC2005]双面棋盘,洛谷P4121,线段树分治+可撤销并查集
正题 这题主要是来练手的,因为没写过可撤销的并查集,大概就是把每一个格子看成一个点,然后格子直接的边有很多的出现区间,把这些出现区间和对应的颜色打到线段树上,然后用可撤销的并查集来维护就可以了. #i ...
- hdu1156(简单线段树 模板题)
敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...
- 线段树模板hdu 1754:I Hate It
I Hate It Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
最新文章
- Ubuntu Linux经典著作
- php新闻模块,新闻模块实现
- mac系统及xcode使用的SVN客户端安装升级
- Linux——VIM学习选取多行(转)
- linux配置iscsi无账号密码,linux4 如何配置iscsi启动器
- udp 协议阻断_应对UDP反射放大攻击的五种常用防护思路
- 计算机科学的鼻祖,现代计算机科学的鼻祖,编程界的上帝
- js实现点击“验证码”开始倒计时
- slopShell:强大的PHP Webshell
- ISO50001认证辅导,ISO50001能源管理体系的框架审核通过系统的提高能源效率和消耗
- VS2015社区版MFC安装
- 怎么教你如何查看电脑的蓝牙版本【解决方案】
- 微信开放平台和公众平台的区别
- 高中计算机应用基础知识课件,计算机应用基础(windows 7+office 2010)课件 第一章 计算机基础知识.ppt.pdf-汇文网...
- Brave vs Google Chrome:哪个浏览器更适合你?
- 利用 API 爬取数据,试着爬取 QQ 音乐流行指数榜
- 数商云:打通产销对接,构建新型数字化农副产品供应链
- 4年程序员30天面试了23家公司,看到公司名单后,网友:羡慕
- 鲲鹏生态“开花结果”,这一次是鹏城云脑Ⅱ
- 计算机xp系统恢复以前设置,最新版:如何在XP系统计算机上恢复出厂设置?