[用CDQ分治解决区间加&区间求和]【习作】
【前言】
作为一个什么数据结构都不会只会CDQ分治和分块的蒟蒻,面对区间加&区间求和这么难的问题,怎么可能会写线段树呢
于是,用CDQ分治解决区间加&区间求和这篇习作应运而生
【Part.I】区间加&区间求和的数据结构做法
【一】线段树
裸题...
1141ms
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define lc x<<1
#define rc x<<1|1
#define mid ((l+r)>>1)
#define lson lc, l, mid
#define rson rc, mid+1, r
typedef long long ll;
const int N=1e5+;
inline ll read(){
char c=getchar();ll x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} int n,Q,op,x,y;
struct SegmentTree{
struct meow{ ll sum, tag; } t[N<<];
inline void paint(int x,int l,int r,ll v){
t[x].tag+= v;
t[x].sum+= (r-l+)*v;
}
inline void pushDown(int x,int l,int r){
if(t[x].tag){
paint(lson, t[x].tag);
paint(rson, t[x].tag);
t[x].tag=;
}
}
void build(int x,int l,int r){
if(l==r) t[x].sum=read();
else{
build(lson);
build(rson);
t[x].sum=t[lc].sum+t[rc].sum;
}
}
void Add(int x,int l,int r,int ql,int qr,ll v){
if(ql<=l && r<=qr) paint(x,l,r,v);
else{
pushDown(x,l,r);
if(ql<=mid) Add(lson, ql, qr, v);
if(mid<qr) Add(rson, ql, qr, v);
t[x].sum=t[lc].sum+t[rc].sum;
}
}
ll Que(int x,int l,int r,int ql,int qr){
if(ql<=l && r<=qr) return t[x].sum;
else{
pushDown(x,l,r);
ll ans=;
if(ql<=mid) ans+=Que(lson, ql, qr);
if(mid<qr) ans+=Que(rson, ql, qr);
return ans;
}
}
}S;
int main(){
//freopen("in","r",stdin);
n=read(); Q=read();
S.build(,,n);
while(Q--){
op=read();x=read();y=read();
if(op==) S.Add(,,n,x,y,read() );
else printf("%lld\n", S.Que(,,n,x,y) );
}
}
SegmentTree
【二】树状数组
考虑每个位置的贡献,维护$a[i]$和$i*a[i]$
477ms
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1e5+;
inline ll read(){
char c=getchar();ll x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} int n,Q,op,x,y;
struct meow{
ll c[N];
inline void add(int p,ll v) {for(;p<=n;p+=p&-p) c[p]+=v;}
inline ll sum(int p) {ll re=; for(;p;p-=p&-p) re+=c[p]; return re;}
inline ll sum(int l,int r) {return sum(r)-sum(l-);}
}C1,C2;
struct BinaryIndexTree{
inline void Add(int l,int r,ll v){
C1.add(l,v); C1.add(r+,-v);
C2.add(l,l*v); C2.add(r+,-(r+)*v);
}
inline ll Que(int l,int r){
return (r-l+)*C1.sum(,l) + (r+)*C1.sum(l+,r) - C2.sum(l+,r);
}
}A;
int main(){
// freopen("in","r",stdin);
n=read(); Q=read();
for(int i=;i<=n;i++) A.Add(i,i,read() );
while(Q--){
op=read();x=read();y=read();
if(op==) A.Add(x,y,read() );
else printf("%lld\n", A.Que(x,y) );
}
}
BinaryIndexTree
【Part.II】区间加&区间求和的CDQ分治做法
首先我们明确CDQ分治是什么 参见[偏序关系与CDQ分治]【学习笔记】
用CDQ分治解决单点加&区间求和 区间加&单点求值 是很容易的,但要两个都是区间就不太方便了
但经过两个多小时的研究,终于做出来啦
区间加&区间求和可以算是二维偏序问题,可以只用排序和CDQ分治不借助任何数据结构解决
首先把修改和询问都拆成两个
我们对时间排序,对位置进行CDQ分治
问题在于对于$[l,r]$这样一个加操作,$r<p$的当然很方便了,但对$l \le p \le r$的每个$p$贡献都是不一样的
一开始困扰了我好久
突然想到可以维护一个$val$表示当前每在位置上移动一个距离总体的贡献应该改变多少,再维护一个$last$表示上一个位置
然后更新当前的贡献时用$val$乘移动的距离就可以了
其实本质就是得到了计算$[l,mid]$中修改的贡献后每一个位置的值应该是多少
问题解决!撒花
1251ms 时间垫底了.....
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1e5+;
inline ll read(){
char c=getchar();ll x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} int n,Q,m,op,l,r;
ll a[N],v,ans[N];
struct meow{
int x,type,qid; ll v;
meow(){}
meow(int b,int c,int d,ll e):x(b),type(c),qid(d),v(e){}
bool operator <(const meow &r) const {return x==r.x ? type<r.type : x<r.x;}
}q[N<<],t[N<<]; void CDQ(int l,int r){
if(l==r) return ;
int mid=(l+r)>>;
CDQ(l, mid); CDQ(mid+, r);
int i=l, j=mid+, p=l;
ll now=, val=; int last=;
while(i<=mid || j<=r){
if(j>r || (i<=mid && q[i]<q[j]) ){
now+=(q[i].x-last)*val; last=q[i].x;
if(q[i].type!=) val+=q[i].v;
t[p++]=q[i++];
}else{
now+=(q[j].x-last)*val; last=q[j].x;
if(!q[j].type) ans[ q[j].qid ]+= now*q[j].v ;
t[p++]=q[j++];
}
}
for(int i=l;i<=r;i++) q[i]=t[i];
} int main(){
freopen("in","r",stdin);
n=read(); Q=read(); int p=;
for(int i=;i<=n;i++) v=read(), q[++m]=meow(i-,-,,v), q[++m]=meow(i,,,-v); for(int i=;i<=Q;i++){
op=read(); l=read(); r=read();
if(op==) v=read(), q[++m]=meow(l-,-,,v), q[++m]=meow(r,,,-v);
else p++, q[++m]=meow(l-,,p,-), q[++m]=meow(r,,p,);
}
CDQ(,m);
for(int i=;i<=p;i++) printf("%lld\n",ans[i]);
}
CDQ分治
【Part.III】应用
这玩意常数又大又需要离线有什么用啊
1.我们可以出题坐标特别大强制线段树来离线离散化
2.可以推广到一系列区间修改与查询问题
[用CDQ分治解决区间加&区间求和]【习作】的更多相关文章
- 「模板」 线段树——区间乘 && 区间加 && 区间求和
「模板」 线段树--区间乘 && 区间加 && 区间求和 原来的代码太恶心了,重贴一遍. #include <cstdio> int n,m; long l ...
- cdq分治解决三维偏序
问题背景 在三维坐标系中有n个点,坐标为(xi,yi,zi). 定义一个点A比一个点B小,当且仅当xA<=xB,yA<=yB,zA<=zB.问对于每个点,有多少个点比它小.(n< ...
- 【BZOJ】1798: [Ahoi2009]Seq 维护序列seq 线段树多标记(区间加+区间乘)
[题意]给定序列,支持区间加和区间乘,查询区间和取模.n<=10^5. [算法]线段树 [题解]线段树多重标记要考虑标记与标记之间的相互影响. 对于sum*b+a,+c直接加上即可. *c后就是 ...
- cdq分治解决区间问题
如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含 ...
- COGS.1317.数列操作c(分块 区间加 区间求和)
题目链接 #include<cmath> #include<cstdio> #include<cctype> #include<algorithm> u ...
- P4315 月下“毛景树” (树链剖分+边剖分+区间覆盖+区间加+区间最大值)
题目链接:https://www.luogu.org/problem/P4315 题目大意: 有N个节点和N-1条树枝,但节点上是没有毛毛果的,毛毛果都是长在树枝上的.但是这棵“毛景树”有着神奇的魔力 ...
- CDQ 分治解决和点对有关的问题
具体可以去这篇博客学习: https://oi-wiki.org/misc/cdq-divide/
- 陌上花开——CDQ分治
传送门 “CDQ分治”从来都没有听说过,写了这题才知道还有这么神奇的算法. (被逼无奈).w(゚Д゚)w 于是看了不少dalao的博客,对CDQ算法粗浅地了解了一点.(想要了解CDQ的概念,可以看下这 ...
- [学习笔记]CDQ分治和整体二分
序言 \(CDQ\) 分治和整体二分都是基于分治的思想,把复杂的问题拆分成许多可以简单求的解子问题.但是这两种算法必须离线处理,不能解决一些强制在线的题目.不过如果题目允许离线的话,这两种算法能把在线 ...
随机推荐
- ubuntu sendmail配置发送邮件
ubuntu中sendmail函数可以很方便的发送邮件,ubuntu sendmail先要安装两个包. 必需安装的两个包: 代码 sudo apt-get install sendmail sudo ...
- cookie 和 session的区别
一.总结: 1.cookie数据存放在客户的浏览器上,session数据放在服务器上. 2.cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗 考虑到安全应当使用ses ...
- 浅析@Deprecated,调用方法时出现横线划掉样式
Deprecated 这个注释是一个标记注释.所谓标记注释,就是在源程序中加入这个标记后,并不影响程序的编译,但有时编译器会显示一些警告信息. 那么Deprecated注释是什么意思呢?如果你经常使用 ...
- 将项目(代码)从GitHub上克隆(下载)到本地仓库
要将项目从GitHub上克隆到本地,首先你得下载并安装好git for window. 下载地址:http://www.xp510.com/xiazai/Application/other/30988 ...
- Python3 引入模块的方法
例子 import random 产生随机整数 import random secret = random.randint(0,10)
- 利用xcode生成的app生成可以在iphone和itouch上运行的ipa安装包
在编译好的真机版目录下的.app文件,至于生成真机可以运行的app的方法,有两种方式,一种是交99美元获得一个证书,另外一种是破解的方式,在此不再详述,本文假设你已经生成了真机上可以运行的app包了( ...
- Codeforces 448 D. Multiplication Table 二分
题目链接:D. Multiplication Table 题意: 给出N×M的乘法矩阵要你求在这个惩罚矩阵中第k个小的元素(1 ≤ n, m ≤ 5·10^5; 1 ≤ k ≤ n·m). 题解: n ...
- salesforce零基础学习(八十一)更改标准字段的label名称(Admin)
我们在开发中往往需要考虑国际化功能,salesforce 提供了国际化功能,在search部分搜索translate,便可以找到translate部分,从而对需要的进行translate.比如pick ...
- 前端css常用class命名id命名
1.常用id的命名: (1)页面结构 容器: container 页头:header 内容:content/container 页面主体:main 页尾:footer 导航:nav 侧栏:sideba ...
- Spring简单的REST例子
控制器处理 HTTP 的4个主要方法 GET,POST,PUT,DELETE 使用@PathVariable注解获取URL中的参数 import javax.inject.Inject; import ...