【题解】P3373 【模板】线段树 2
线段树解法
好丢脸,这个题做了一下午,调试了三个多小时......
先讲讲解题思路
既然这里是线段树,就要用到lazy—tag。又有加法又有乘法的话,就要用到两个lazy-tag,分别用数组jia[]和chng[]表示。线段树用数组t[]存。
我们让lazy-tag还原数值时,先乘chng[],再加jia[](人为规定,这样好算)
怎么维护lazy-tag?
加法
void add( k, l, r, x, y, delta)
{
函数的作用是在编号为k,区间是[l,r]的线段树里,给区间[x,y]里的每一个数加上delta。
如果当前区间[l,r]和目标区间[x,y]完全重合,就要在当前这颗编号为k的树上标记。
首先jia[k]要加上delta,表示当前区间[l,r](即[x,y])内的每一个数都加了delta;
然后要修改t[k]的值,也就是加上区间内增加的总数,即t[k]+=delta*(r-l+1);
return。
如果当前区间不与目标区间完全重合,就要对子树操作。
首先,标记下传,用pushdown()函数将树k的标记全数下传给两个儿子k*2和k*2+1;
然后,先取mid=(l+r)>>1,判断一下目标区间是在当前区间的左子树区间、还是右子树区间、还是左右都有;
(如果y<=mid,那么目标区间一定只在左子树里;如果x>=mid+1,那么目标区间一定只在右子树里;如果上述两个条件都不满足,那就是分布在左右区间了);
这样按分类递归子树,递归完了后标记就在子树里存好了,这样子树的值变了,接着别忘了更新t[k]的值(t[k]=t[k*2]+t[k*2+1]);
return。
}
这里要明确,当父亲k替儿子记录下了加法标记时,儿子啥值也没改,还是憨憨的和啥也没加一样。乘法也如此。
这里是代码
void add(long long k, long long l, long long r, long long x, long long y, long long delta)
{
if(l==x&&r==y){
jia[k]+=delta;
t[k]+=delta*(r-l+1);
return;
}
long long mid=(l+r)>>1;
pushdown(k,l,r);
if(x>=mid+1) add(k*2+1,mid+1,r,x,y,delta);
else if(y<=mid) add(k*2,l,mid,x,y,delta);
else{
add(k*2,l,mid,x,mid,delta);
add(k*2+1,mid+1,r,mid+1,y,delta);
}
t[k]=t[k*2]+t[k*2+1];
t[k]%=p;
}
乘法
void cheng(k,l,r,x,y,ch)
{
给区间[x,y]里每一个数都乘ch
给区间里的每一个数乘ch,如何标记在lazy-tag里?
这个数啊,chng[]标记已经标记着他要乘chng[k],jia[]标记着他再要加jia[k],这下子又要乘ch,根据分配率,
(t[k]*chng[k]+jia[k])*ch=t[k]*chng[k]*ch+jia[k]*ch
这意味着标记乘ch时,我们要让chng[k]和jia[k]都乘上ch。
具体操作时,还是要和加法一样分类处理,最后别忘更新t[k]。
}
代码
void cheng(long long k, long long l, long long r, long long x, long long y, long long ch)
{
if(l==x&&r==y){
t[k]*=ch;
t[k]%=p;
chng[k]*=ch;
chng[k]%=p;
jia[k]*=ch;
jia[k]%=p;
return;
}
pushdown(k,l,r);
long long mid=(l+r)>>1;
if(x>=mid+1) cheng(k*2+1,mid+1,r,x,y,ch);
else if(y<=mid) cheng(k*2,l,mid,x,y,ch);
else {
cheng(k*2,l,mid,x,mid,ch);
cheng(k*2+1,mid+1,r,mid+1,y,ch);
}
t[k]=t[k*2]+t[k*2+1];
t[k]%=p;
}
pushdown
维护lazy-tag需要pushdown,询问输出也要pushdown
pushdown是标记下传操作,意味着当前树k不再保留所在区间的加乘标记,也就是说,pushdown后,jia[k]=0,chng[k]=1。
下传到子树时,子树的加、乘标记和本身的值都要改变(终于不是憨憨的啥也不知道了!)。
void pushdown(k,l,r)
{
更新左二子
更新儿子的标记,和void cheng()维护乘法标记一样,用到分配率
t[k*2]'->t[k*2]*chng[k*2]+jia[k*2],这里chng[k]表示区间内每一个数都要乘chng[k],
所以t[k*2]'->(t[k*2]*chng[k*2]+jia[k*2])*chng[k]=t[k*2]*chng[k*2]*chng[k]+jia[k*2]*chng[k]
这意味着chng[k*2]更新为chng[k*2]*chng[k],jia[k*2]更新为jia[k*2]*chng[k]。
然后更新t[2*k]的值
t[2*k]'->当前树的值*乘法标记+加法标记*区间长度
更新完左右儿子后别忘还原父亲的标记
}
this is the code
void pushdown(long long k, long long l, long long r)
{
long long mid=(l+r)>>1;
jia[k*2]=jia[k*2]*chng[k]+jia[k];
jia[k*2]%=p;
jia[k*2+1]=jia[k*2+1]*chng[k]+jia[k];
jia[k*2+1]%=p;
chng[k*2]*=chng[k];
chng[k*2]%=p;
chng[k*2+1]*=chng[k];
chng[k*2+1]%=p;
t[k*2]=t[k*2]*chng[k]+jia[k]*(mid-l+1);// cout<<t[k*2]<<" "<<chng[k]<<endl;
t[k*2]%=p;
t[k*2+1]=t[k*2+1]*chng[k]+jia[k]*(r-mid);
t[k*2+1]%=p;
jia[k]=0;
chng[k]=1;
}
建树
这个大家都会,直接上代码
void build(long long k, long long l, long long r)
{
jia[k]=0;
chng[k]=1;//标记初始化
if(l==r){
t[k]=a[l]; //直接赋值
return;
}
long long mid=(l+r)>>1;
build(k*2,l,mid);
build(k*2+1,mid+1,r);//递归建树
t[k]=(t[k*2]+t[k*2+1])%p;//给父亲赋值
}
询问
void query(k,l,r,x,y)
{
还是分类讨论
1.[l,r]和[x,y]完全重合,直接返回t[k]
2.既然不完全重合,就先要pushdown,更新子树的值,再分类递归子树
}
code
long long query(long long k, long long l, long long r, long long x, long long y)
{
if(l==x && r==y){
return t[k]%p;
}
pushdown(k,l,r);
long long mid = (l+r)>>1;
if(x>=mid+1) return query(k*2+1,mid+1,r,x,y)%p;
else if(y<=mid) return query(k*2,l,mid,x,y)%p;
else return (query(k*2,l,mid,x,mid)+query(k*2+1,mid+1,r,mid+1,y))%p;
}
这样这个题大体就成了,然鹅,想要AC,还要注意以下三点
1.数组开多大?其实要开n4,开n3也过了,但是开n*2就不行
2.开longlong!开longlong!!
3.随时取模,query返回时也要再取模
代码
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define N 100100
//开longlong
//开4倍
using namespace std;
long long n,m,x,y,k,p,a[N],jia[N*4],chng[N*4],t[N*4];//开4倍
void build(long long k, long long l, long long r)
{
jia[k]=0;
chng[k]=1;
if(l==r){
t[k]=a[l];
return;
}
long long mid=(l+r)>>1;
build(k*2,l,mid);
build(k*2+1,mid+1,r);
t[k]=(t[k*2]+t[k*2+1])%p;
}
void pushdown(long long k, long long l, long long r)
{
long long mid=(l+r)>>1;
jia[k*2]=jia[k*2]*chng[k]+jia[k];
jia[k*2]%=p;
jia[k*2+1]=jia[k*2+1]*chng[k]+jia[k];
jia[k*2+1]%=p;
chng[k*2]*=chng[k];
chng[k*2]%=p;
chng[k*2+1]*=chng[k];
chng[k*2+1]%=p;
t[k*2]=t[k*2]*chng[k]+jia[k]*(mid-l+1);
t[k*2]%=p;
t[k*2+1]=t[k*2+1]*chng[k]+jia[k]*(r-mid);
t[k*2+1]%=p;
jia[k]=0;
chng[k]=1;
}
void add(long long k, long long l, long long r, long long x, long long y, long long delta)
{
if(l==x&&r==y){
jia[k]+=delta;
t[k]+=delta*(r-l+1);
return;
}
long long mid=(l+r)>>1;
pushdown(k,l,r);
if(x>=mid+1) add(k*2+1,mid+1,r,x,y,delta);
else if(y<=mid) add(k*2,l,mid,x,y,delta);
else{
add(k*2,l,mid,x,mid,delta);
add(k*2+1,mid+1,r,mid+1,y,delta);
}
t[k]=t[k*2]+t[k*2+1]; t[k]%=p;
}
void cheng(long long k, long long l, long long r, long long x, long long y, long long ch)
{
if(l==x&&r==y){
t[k]*=ch;
t[k]%=p;
chng[k]*=ch;
chng[k]%=p;
jia[k]*=ch;
jia[k]%=p;
return;
}
pushdown(k,l,r);
long long mid=(l+r)>>1;
if(x>=mid+1) cheng(k*2+1,mid+1,r,x,y,ch);
else if(y<=mid) cheng(k*2,l,mid,x,y,ch);
else {
cheng(k*2,l,mid,x,mid,ch);
cheng(k*2+1,mid+1,r,mid+1,y,ch);
}
t[k]=t[k*2]+t[k*2+1]; t[k]%=p;
}
long long query(long long k, long long l, long long r, long long x, long long y)
{
if(l==x && r==y){
return t[k]%p;
}
pushdown(k,l,r);
long long mid = (l+r)>>1;
if(x>=mid+1) return query(k*2+1,mid+1,r,x,y)%p;
else if(y<=mid) return query(k*2,l,mid,x,y)%p;
else return (query(k*2,l,mid,x,mid)+query(k*2+1,mid+1,r,mid+1,y))%p;
}
int main()
{
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,1,n);
for(int i=1;i<=m;i++)
{
int tt;
scanf("%d",&tt);
if(tt==1){
scanf("%d%d%d",&x,&y,&k);
cheng(1,1,n,x,y,k);
}
if(tt==2){
scanf("%d%d%d",&x,&y,&k);
add(1,1,n,x,y,k);
}
if(tt==3){
scanf("%d%d",&x,&y);
printf("%d\n",query(1,1,n,x,y));
}
}
return 0;
}
【题解】P3373 【模板】线段树 2的更多相关文章
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- hdu 1754 I Hate It (模板线段树)
http://acm.hdu.edu.cn/showproblem.php?pid=1754 I Hate It Time Limit: 9000/3000 MS (Java/Others) M ...
- hdu3966 树链剖分点权模板+线段树区间更新/树状数组区间更新单点查询
点权树的模板题,另外发现树状数组也是可以区间更新的.. 注意在对链进行操作时方向不要搞错 线段树版本 #include<bits/stdc++.h> using namespace std ...
- 【线段树】【P3372】模板-线段树
百度百科 Definition&Solution 线段树是一种log级别的树形结构,可以处理区间修改以及区间查询问题.期望情况下,复杂度为O(nlogn). 核心思想见百度百科,线段树即将每个 ...
- [NOIP10.6模拟赛]2.equation题解--DFS序+线段树
题目链接: 咕 闲扯: 终于在集训中敲出正解(虽然与正解不完全相同),开心QAQ 首先比较巧,这题是\(Ebola\)出的一场模拟赛的一道题的树上强化版,当时还口胡出了那题的题解 然而考场上只得了86 ...
- 算法模板——线段树6(二维线段树:区域加法+区域求和)(求助phile)
实现功能——对于一个N×M的方格,1:输入一个区域,将此区域全部值作加法:2:输入一个区域,求此区域全部值的和 其实和一维线段树同理,只是不知道为什么速度比想象的慢那么多,求解释...@acphile ...
- 【题解】Journeys(线段树优化连边)
[#3073. Pa2011]Journeys (线段树优化连边) 这张图太直观了,直接讲透了线段树优化连边的原理和正确性. 考虑建立两颗线段树,一颗是外向树,一颗是内向树,相当于网络流建模一样,我们 ...
- Gorgeous Sequence 题解 (小清新线段树)
这道题被学长称为“科幻题” 题面 事实上,并不是做法科幻,而是“为什么能这么做?”的解释非常科幻 换句话说,复杂度分析灰常诡异以至于吉如一大佬当场吃书 线段树维护的量:区间和sum,区间最大值max1 ...
- 洛谷题解P4314CPU监控--线段树
题目链接 https://www.luogu.org/problemnew/show/P4314 https://www.lydsy.com/JudgeOnline/problem.php?id=30 ...
- [NOI2016]区间 题解(决策单调性+线段树优化)
4653: [Noi2016]区间 Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 1593 Solved: 869[Submit][Status][ ...
随机推荐
- bash的默认组合键
组合键 组合按键 执行结果 Ctrl+C 终止目前的命令 Ctrl+D 输入结束(EOF),例如邮件结束的时候 Ctrl+M 就是Enter啦! Ctrl+S 暂停屏幕输出 Ctrl+Q 恢复屏幕输出 ...
- 关于Itext 报错-java.lang.NoClassDefFoundError: org/bouncycastle/asn1/ASN1Encodable
如果我们在用iText 做为java 为PDF 文档加水印的时候 报如下异常 java.lang.NoClassDefFoundError: org/bouncycastle/asn1/ASN1Enc ...
- 密码 | 对称加密 - AES
一.AES 算法简介 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准,用来替换 ...
- 移动端保存当前屏幕内容为图片,canvas图片拼接
需求:1.移动端点击分享时,截屏当前屏幕,并保存为图片 2.将截屏的图片与一张二维码图片进行拼接后,生成一张新的图片 技术栈:html2canvas.js.canvas2image.js 代码:(j ...
- 阿望教你用vue写扫雷(超详细哦)
前言 话说阿望还在大学时,某一天寝室突然停网了,于是和室友两人不约而同地打开了扫雷,比相同难度下谁更快找出全部的雷,玩得不亦乐乎,就这样,扫雷伴我们度过了断网的一周,是整整一周啊,不用上课的那种,可想 ...
- oa办公系统是什么?对企业有什么作用?
OA办公系统是指利用计算机网络帮助企业实现办公自动化,用系统软件代替传统的手工工作帮助企业处理内部事务,例如文档共享.部门协作.报销.业务流程等等,最终目的帮助企业提高工作效率,实现利益最大化. 随着 ...
- IDEA需要修改的配置
自动编译开关 忽略大小写开关 智能导包开关 如下图所示,将 自动导入不明确的结构 智能优化包 这两个选项勾上.那么有什么效果呢? 你在代码中,只要敲list,就会出现提示,自动导入java.util. ...
- Tomcat9乱码解决
在tomcat的解压目录下找到conf,打开进入,logging.properties文件,在该文件中,修改 java.util.logging.ConsoleHandler.encoding = U ...
- pandas时间序列常用操作
目录 一.时间序列是什么 二.时间序列的选取 三.时间序列的生成 四.时间序列的偏移量 五.时间前移或后移 五.时区处理 六.时期及算术运算 七.频率转换 一.时间序列是什么 时间序列在多个时间点观察 ...
- HUAWEI MateBook Fn 功能键/热键切换、设置方法
原文地址:https://club.huawei.com/thread-13130964-1-1.html HUAWE MateBook E/X/D的F1.F2 等键默认是热键优先.在热键模式下,要想 ...