bzoj3343 教主的魔法【分块入门】By cellur925
题意:维护一个数列,给出维护区间加法,询问区间内大于等于某个值的元素个数。
算法:分块。因为本题第二问显然可以用二分的思想,但是这貌似并不符合区间可加性,线段树好像就不好用了呢。所以本蒟蒻学习了分块。
这大概是本蒟蒻的第一题正式分块,思想是在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的更多相关文章
- BZOJ3343: 教主的魔法 分块
2016-05-28 10:27:19 题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3343 比较显然的分块题,分块后块内排序,维护整块的附 ...
- P2801 教主的魔法(分块入门)
两个月之前听yyr学长讲的分块,感觉是个很神奇的暴力,但到现在还是懵的一匹 #include<bits/stdc++.h> using namespace std; ; int belon ...
- [bzoj3343]教主的魔法——分块
Brief description 给定一个数列,您需要支持一下两种操作: 给[l,r]同加一个数 询问[l,r]中有多少数字大于或等于v Algorithm analyse 这个题一时想不到什么有效 ...
- BZOJ-3343教主的魔法+分块(大块排序二分)
传送门:https://www.luogu.org/problemnew/show/P2801 参考:http://hzwer.com/2784.html 感觉思路无比清晰:) ps:我在洛谷A的, ...
- bzoj3343: 教主的魔法 分块 标记
修改:两边暴力重构,中间打标记.复杂度:O(n0.5) 查询:中间二分两边暴力.O(n0.5logn0.5) 总时间复杂度O(n*n0.5logn0.5) 空间复杂度是n级别的 标记不用下传因为标记不 ...
- [BZOJ3343]教主的魔法
[BZOJ3343]教主的魔法 试题描述 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.… ...
- Luogu 2801 教主的魔法 | 分块模板题
Luogu 2801 教主的魔法 | 分块模板题 我犯的错误: 有一处l打成了1,还看不出来-- 缩小块大小De完bug后忘了把块大小改回去就提交--还以为自己一定能A了-- #include < ...
- BZOJ 3343: 教主的魔法(分块+二分查找)
BZOJ 3343: 教主的魔法(分块+二分查找) 3343: 教主的魔法 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1172 Solved: ...
- 【BZOJ-3343】教主的魔法 分块
3343: 教主的魔法 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 950 Solved: 414[Submit][Status][Discuss ...
随机推荐
- 走入asp.net mvc不归路:[5]Action的返回
asp.net mvc提供了多种返回方式,一方面使得视图可以重用,另一方面灵活强大,有直接返回视图,返回Json,返回文件流,返回到相同Controller的Action,返回到另一个Controll ...
- mysql 环境变量之 group_concat_max_len
今天使用mysql group_concat()函数,对查询的数据进行字符串连接操作. 不过由于查询的结果较多,连接后的结果很长导致不能完全显示. 查询手册发现如下说明: (先说说group_conc ...
- (testng多个class文件执行时混乱,不是等一个class内的所有methods执行完再去执行下一个class内的内容)问题的解决
问题描述如下: We use TestNG and Selenium WebDriver to test our web application. Now our problem is that we ...
- CentOS7虚拟机桥接设置及问题
转载请注明出处:jiq•钦's technical Blog 今天在VMWare WorkStation9中安装了CentOS7虚拟机. 虚拟机与宿主机网络连接有三种方式:桥接模式.NAT模式和Ho ...
- nodejs什么值得买自动签到自动评论定时任务
本项目是基于nodejs开发,实现的功能是,什么值得买自动签到,自动评论功能,自动发邮件,支持多人多账号运行 目的是为了,解放双手,轻松获取什么值得买的经验和积分,得到更高的等级,从而突破很会员等级限 ...
- HDU 6143 Killer Names DP+快速密
Killer Names Problem Description > Galen Marek, codenamed Starkiller, was a male Human apprentice ...
- spring、spring MVC、spring Boot
Spring 是一个“引擎” Spring MVC 是基于 Spring 的一个 MVC 框架 Spring Boot 是基于 Spring4 的条件注册的一套快速开发整合包 Spring 最初利用“ ...
- linq to xml There are multiple root elements.
添加xml结点的时候 var temp2 = temp1.Element("staticContent"); if (temp2 != null) { string str = & ...
- iOS成员变量、实例变量、属性变量三者的联系与区别
一.类Class中的属性property 在ios第一版中: 我们为输出口同时声明了属性和底层实例变量,那时,属性是oc语言的一个新的机制,并且要求你必须声明与之对应的实例变量,例如: 注意:(这个是 ...
- 「 LuoguT37042」 求子序列个数
Description 给定序列 A, 求出 A 中本质不同的子序列 (包含空的子序列) 个数模 10^9+ 7 的结果. 一个序列 B 是 A 的子序列需要满足 A 删掉某些元素后能够得到 B. 两 ...