[用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\) 分治和整体二分都是基于分治的思想,把复杂的问题拆分成许多可以简单求的解子问题.但是这两种算法必须离线处理,不能解决一些强制在线的题目.不过如果题目允许离线的话,这两种算法能把在线 ...
随机推荐
- 2017ecjtu-summer training #5 UVA10382
题意 问最少可用几个圆覆盖矩形区域. 解析 将圆形转换成矩形有效区域,直径小于等于宽度的圆不考虑,从而转化成区间覆盖问题,然后贪心出最少圆. 贪心思想 每次选择出区域左界比上次选出的区域右界小的且区域 ...
- LightOJ DNA Prefix(字典树+dfs)
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=121897#problem/F F - DNA Prefix Time Limit:200 ...
- SecureCRT连接虚拟机中的Linux系统(Ubuntu)_Linux教程
有道云笔记链接地址: https://note.youdao.com/share/?id=826781e7ca1fd1223f6a43f4dc2c9b5d&type=note#/
- Spring框架学习笔记(8)——AspectJ实现AOP
使用代理对象实现AOP虽然可以满足需求,但是较为复杂,而Spring提供一种简单的实现AOP的方法AspectJ 同样的计算器的DEMO 首先配置applicationContext.xml < ...
- 从零开始学习前端JAVASCRIPT — 1、JavaScript基础
1:定义:javascript是一种弱类型.动态类型.解释型的脚本语言. 弱类型:类型检查不严格,偏向于容忍隐式类型转换. 强类型:类型检查严格,偏向于不容忍隐式类型转换. 动态类型:运行的时候执行类 ...
- dig命令
dig(域信息搜索器)命令是一个用于询问 DNS 域名服务器的灵活的工具.它执行 DNS 搜索,显示从受请求的域名服务器返回的答复.多数 DNS 管理员利用 dig 作为 DNS 问题的故障诊断, ...
- console.log 用法
转自http://www.cnblogs.com/ctriphire/p/4116207.html 大家都有用过各种类型的浏览器,每种浏览器都有自己的特色,本人拙见,在我用过的浏览器当中,我是最喜欢C ...
- SVN的安装和配置
SVN为程序开发团队常用的代码管理,版本控制软件:下面我们来介绍TortoiseSVN的安装,和其服务器的搭建:(下面为windows 64位系统下的搭建) 闲来无事,就在本地搭建了一个SVN环境,网 ...
- 分布式CAP原理
根据维基百科定义[CAP] 根据定理,一个分布式系统最多只能满足其中两项, 不可能同时满则C-A-P三项 首先说一下对各项原则的理解 (1)一致性C: 单机环境下, 数据只有一份,所有的客户端访问的是 ...
- 解决vue路径中#号
在router文件夹下的js文件中,更改配置增加 mode: 'history'; vue-router官方文档:https://router.vuejs.org/zh-cn/essentials/h ...