题意:维护一个数列,给出维护区间加法,询问区间内大于等于某个值的元素个数。

算法:分块。因为本题第二问显然可以用二分的思想,但是这貌似并不符合区间可加性,线段树好像就不好用了呢。所以本蒟蒻学习了分块。

这大概是本蒟蒻的第一题正式分块,思想是在hzwer学长的分块入门学的==。

什么是分块?我们维护数列(貌似树上也可以)信息时可以先采用分治的思想,把数列分成若干连续的块,维护信息可以统一在块上进行维护。假如有一个n元素的数列,根据均值不等式的数学知识(并不会证明),我们把每个块的大小设为根号n可以达到最右效果。但是显然有时根号n并不是整数==,没有关系,我们的数列上大部分都在整块上,只有小部分被遗弃的元素在不完整的块中。

我们进行区间操作时,把在整块中的元素进行整体操作,那些在不完整的块中的元素进行暴力操作

总结来说,就是“大块维护,小段朴素”,这也是分块最基本的思想。

分块的复杂度(根号算法)虽然较其他数据结构可能略高了一些,却是打暴力的好方法,思维较简单,处理问题的范围更广,码量...可能略大(?)

回到本题的两个操作:

区间加法:对于一段区间,如果存在被整块覆盖的情况,直接在整块的加法标记上记录,不需在原序列上改动,查询时在加上加法标记;而在不完整的块中的元素,直接暴力修改。

查询大于等于X的元素个数:想一想如果数列是无序的,我们分的块就完全没用了。但是如果我们在预处理时以及修改时对每个整块进行排序,查询时调用lower_bound/手写二分查找,问题就能轻松得到解决。那些在不完整块中的元素,同理,暴力枚举统计。

综上,我们不难发现,使用分块算法需要或者只需要想的两个关键:

  怎么维护整块?怎么维护不完整的块?

分块的更多代码实现细节:

  (几乎每到分块题都要的)bl[i]记录i在第几个块

  bl[i]*blo(blo=sqrt(n))可以得出当前位置(块的右边界),在此基础上变形

  右边界:取min,bl[l]*blo,n(防止越界)

Code

 // luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector> using namespace std; int n,Q,blo;
int val[],bl[],addtag[];
char opt[];
vector<int>cnt[]; void update(int x)
{
cnt[x].clear();
for(int i=(x-)*blo+;i<=min(x*blo,n);i++)
cnt[x].push_back(val[i]);
sort(cnt[x].begin(),cnt[x].end());
} void add(int l,int r,int p)
{
if(l==r) val[l]+=p;
else
{
for(int i=l;i<=min(bl[l]*blo,r);i++)
val[i]+=p;
update(bl[l]);
if(bl[l]!=bl[r])
{
for(int i=(bl[r]-)*blo+;i<=r;i++)
val[i]+=p;
update(bl[r]);
}
for(int i=bl[l]+;i<=bl[r]-;i++)
addtag[i]+=p;
}
} int query(int l,int r,int p)
{
int ans=;
for(int i=l;i<=min(bl[l]*blo,r);i++)
if(val[i]+addtag[bl[l]]>=p) ans++;
if(bl[l]!=bl[r])
{
for(int i=(bl[r]-)*blo+;i<=r;i++)
if(val[i]+addtag[bl[r]]>=p) ans++;
}
for(int i=bl[l]+;i<=bl[r]-;i++)
{
int tmp=p-addtag[i];
int pos=lower_bound(cnt[i].begin(),cnt[i].end(),tmp)-cnt[i].begin();
ans+=blo-pos;
// ans+=find(i,tmp);
}
return ans;
} int main()
{
scanf("%d%d",&n,&Q);
for(int i=;i<=n;i++) scanf("%d",&val[i]);
blo=sqrt(n);
for(int i=;i<=n;i++)
{
bl[i]=(i-)/blo+;
cnt[bl[i]].push_back(val[i]);
}
for(int i=;i<=bl[n];i++)
sort(cnt[i].begin(),cnt[i].end());
while(Q--)
{
scanf("%s",opt+);
int x=,y=,z=;
scanf("%d%d%d",&x,&y,&z);
if(opt[]=='M')
add(x,y,z);
else if(opt[]=='A')
printf("%d\n",query(x,y,z));
}
return ;
}

代码纯享版

 // luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector> using namespace std; int n,Q,blo;
int val[],bl[],addtag[];
char opt[];
vector<int>cnt[]; void update(int x)//针对散块
{//清空,重新读入
cnt[x].clear();
for(int i=(x-)*blo+;i<=min(x*blo,n);i++)
cnt[x].push_back(val[i]);
sort(cnt[x].begin(),cnt[x].end());
} void add(int l,int r,int p)
{
if(l==r) val[l]+=p;
else
{
for(int i=l;i<=min(bl[l]*blo,r);i++)
val[i]+=p;//左散块
update(bl[l]);//重新排序
if(bl[l]!=bl[r])
{//右散块
for(int i=(bl[r]-)*blo+;i<=r;i++)
val[i]+=p;
update(bl[r]);
}
//整块
for(int i=bl[l]+;i<=bl[r]-;i++)
addtag[i]+=p;
}
} int query(int l,int r,int p)
{
int ans=;
//左半散块
for(int i=l;i<=min(bl[l]*blo,r);i++)
if(val[i]+addtag[bl[l]]>=p) ans++;
if(bl[l]!=bl[r])// !
{//右半散块
for(int i=(bl[r]-)*blo+;i<=r;i++)
if(val[i]+addtag[bl[r]]>=p) ans++;
}
for(int i=bl[l]+;i<=bl[r]-;i++)//整块
{
int tmp=p-addtag[i];//求>=p,则先把加法标记减去
int pos=lower_bound(cnt[i].begin(),cnt[i].end(),tmp)-cnt[i].begin();
ans+=blo-pos;//注意这句 精髓
// ans+=find(i,tmp);
}
return ans;
} int main()
{
scanf("%d%d",&n,&Q);
for(int i=;i<=n;i++) scanf("%d",&val[i]);
blo=sqrt(n);
for(int i=;i<=n;i++)
{
bl[i]=(i-)/blo+;
cnt[bl[i]].push_back(val[i]);//记录每个块中元素
}
//对每个块中元素进行排序
for(int i=;i<=bl[n];i++)
sort(cnt[i].begin(),cnt[i].end());
while(Q--)
{
scanf("%s",opt+);
int x=,y=,z=;
scanf("%d%d%d",&x,&y,&z);
if(opt[]=='M')
add(x,y,z);
else if(opt[]=='A')
printf("%d\n",query(x,y,z));
}
return ;
}

贴心注释版

然鹅不吸氧会T掉一个点,可能是我lowerbound常数的锅锅?讨论里有人说要用longlong结果我用了longlong只会带来无端的CE/WA/RE。

Just be cautious.

bzoj3343 教主的魔法【分块入门】By cellur925的更多相关文章

  1. BZOJ3343: 教主的魔法 分块

    2016-05-28  10:27:19 题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3343 比较显然的分块题,分块后块内排序,维护整块的附 ...

  2. P2801 教主的魔法(分块入门)

    两个月之前听yyr学长讲的分块,感觉是个很神奇的暴力,但到现在还是懵的一匹 #include<bits/stdc++.h> using namespace std; ; int belon ...

  3. [bzoj3343]教主的魔法——分块

    Brief description 给定一个数列,您需要支持一下两种操作: 给[l,r]同加一个数 询问[l,r]中有多少数字大于或等于v Algorithm analyse 这个题一时想不到什么有效 ...

  4. BZOJ-3343教主的魔法+分块(大块排序二分)

    传送门:https://www.luogu.org/problemnew/show/P2801 参考:http://hzwer.com/2784.html  感觉思路无比清晰:) ps:我在洛谷A的, ...

  5. bzoj3343: 教主的魔法 分块 标记

    修改:两边暴力重构,中间打标记.复杂度:O(n0.5) 查询:中间二分两边暴力.O(n0.5logn0.5) 总时间复杂度O(n*n0.5logn0.5) 空间复杂度是n级别的 标记不用下传因为标记不 ...

  6. [BZOJ3343]教主的魔法

    [BZOJ3343]教主的魔法 试题描述 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.… ...

  7. Luogu 2801 教主的魔法 | 分块模板题

    Luogu 2801 教主的魔法 | 分块模板题 我犯的错误: 有一处l打成了1,还看不出来-- 缩小块大小De完bug后忘了把块大小改回去就提交--还以为自己一定能A了-- #include < ...

  8. BZOJ 3343: 教主的魔法(分块+二分查找)

    BZOJ 3343: 教主的魔法(分块+二分查找) 3343: 教主的魔法 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1172  Solved:  ...

  9. 【BZOJ-3343】教主的魔法 分块

    3343: 教主的魔法 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 950  Solved: 414[Submit][Status][Discuss ...

随机推荐

  1. uboot1.1.6中启动流程

    U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶段的功能 Ø 硬件设备初始化 Ø 加载U-Boot第二阶段代码到RAM空间 Ø 设置好栈 Ø 跳转到第二阶段代码入口 (2 ...

  2. startActivity、 startActivityForResult 、广播的使用

    前言 近期忙着跟项目.好久没有写文字了.今天一个群里面的童鞋问到一个关于不同界面间传值的问题,借这个背景,写一段关于" startActivity. startActivityForResu ...

  3. Sublime Text2-Control Package---ShinePans

    1.打开sublime Text2 2.菜单条中的preference>>BrowsePackages 3.退到上一级打开Installed Packages 4.拷贝文件到此目录 (Pa ...

  4. Eclipse中的Web项目自己主动部署到Tomcat

    一.原因. 1.写java程序有一段时间了,但非常久没用eclipse了.所以使用eclipse编写的web项目部署到tomcat 的方式也不是非常清楚,以下记录一下将Eclipse 上的web项目自 ...

  5. 三分钟教你学Git(十四) 之 线下传输仓库

    有时候还有一个人不能从远程直接clone仓库或者说由于非常大,clone非常慢或其他原因.我们能够使用bundle命令将Git仓库打包,然后通过U盘或者是其他介质拷贝给他,这样他拿到打包好的仓库后能够 ...

  6. 更多的使用自定义元素(CustomElement)。

    更多的使用自定义元素(CustomElement).

  7. monitor and move the log content to our big data system

    Apache Flume HDFS Sink Tutorial | HowToProgram https://howtoprogram.xyz/2016/08/01/apache-flume-hdfs ...

  8. hadoop yarn namenode datanoe 启动异常问题解决 分析日志

    cat logs/hadoop-root-datanode-hadoop1.log ********************************************************** ...

  9. powerdesigner 导入mysql数据库(步骤及注意点)

    参考博客 PowerDesigner中导入MYSQL数据库结构的步骤 mysql ODBC 在64位下提示找不到odbc驱动问题 PowerDesigner逆向工程导入MYSQL数据库总结

  10. Linux内核日志开关

    Linux内核日志开关 1.让pr_debug能输出 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -59,7 +59,7 ...