[BZOJ 2989]数列(二进制分组+主席树)
[BZOJ 2989]数列(二进制分组+主席树)
题面
给定一个长度为n的正整数数列a[i]。
定义2个位置的graze值为两者位置差与数值差的和,即graze(x,y)=|x-y|+|a[x]-a[y]|。
2种操作(k都是正整数):
1.Modify x k:将第x个数的值修改为k。
2.Query x k:询问有几个i满足graze(x,i)<=k。因为可持久化数据结构的流行,询问仅要考虑当前数列,还要考虑任意历史版本,即统计任意位置上出现过的任意数值与当前的a[x]的graze值<=k的对数。(某位置多次修改为同样的数值,按多次统计)
分析
预备知识
观察到\(|x-y|+|a[x]-a[y]|\),我们想到曼哈顿距离。于是我们把(x,a[x])看作一个二维平面上的点,而查询的是到平面上一定点距离<=k的点的个数。同时这样还避免了可持久化的问题,因为把a[x]修改成k,相当于新建了一个点。
到平面上一定点曼哈顿距离=k的点的集合(曼哈顿距离下的圆)是一个斜45°,边长为\(\frac{\sqrt{2}}{2}k\)的正方形。我们求的答案就是这个正方形里的点的个数
[图片转载自https://www.cnblogs.com/SGCollin/p/9636955.html]
这样的点距离不太好求。但是我们知道切比雪夫距离下的圆是一个四边都平行坐标轴的正方形。因此我们可以把曼哈顿距离转成对应的切比雪夫距离。注:\((x_1,y_1)和(x_2,y_2)的切比雪夫距离是max(|x_1-x_2|,|y_1-y_2|)\)
考虑把两个点转化成\((x_1+y_1,x_1-y_1),(x_2+y_2,x_2-y_2)\),容易发现新点的切比雪夫距离和原来点的曼哈顿距离相同.
那么我们只要把询问和修改的每个点都转成(x+y,x-y)的形式,然后问题就变成在把原来查询的正方形转换后新的正方形里点的个数。即查询(x-k,y-k)到(x+k,y+k)的二维前缀和
正文
当然可以离线cdq分治,方法见这篇博客。但是我们这里给出一个在线做法。
考虑没有修改的情况,把x坐标离散化,以x坐标为下标建n棵可持久化线段树维护前缀和,y坐标为线段树下标。在第x-k(离散化后)到第x+k棵线段树上查区间[y-k,y+k]就可以得到二维前缀和 .
有修改的情况下我们可以运用二进制分组的方法。
二进制分组的基本思想是把修改操作按二进制分组,遇到修改就在尾部加一个,并与之前的合并。因此,组数只有\(O(\log m)\)个
比如之前有23(16+4+2+1)个,加了1个后就变成16+4+2+1+1,把1和1合并变成 2,2和2合并...最后就变成了24(16+8),其中16对应的块存储前16组修改操作之后数据结构的状态。合并的时候暴力插入,然后重构合并块,相当于把原来的修改操作都撤销然后再加回去。
遇到查询就在每个组内查询,再加起来就好了。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<utility>
#include<vector>
#define maxn 100000
#define maxlogn 32
#define INF 0x3f3f3f3f
using namespace std;
int n,q;
int a[maxn+5];
struct mem_pool{//手写内存池,回收删除的点防止MLE
int top=0;
int stk[maxn*2*maxlogn+5];
inline void ini(int n){
for(int i=n;i>=1;i--) stk[++top]=i;
}
inline int New(){
return stk[top--];
}
inline void del(int x){
stk[++top]=x;
}
}pool;
//把x坐标离散化,以x坐标为下标建n棵可持久化线段树维护前缀和,y坐标为线段树下标
//在第x-k(离散化后)到第x+k棵线段树上查区间[y-k,y+k]就可以得到二维前缀和
int root[maxlogn+5][maxn*2+5];//root[i][j],第i个分组区间里面第j小的x坐标对于的线段树
struct persist_segment_tree{
#define lson(x) tree[x].ls
#define rson(x) tree[x].rs
struct node{
int ls;
int rs;
int val;
}tree[maxn*2*maxlogn+5];
bool is_del[maxn*2*maxlogn+5];
//因为一个点不能被删除两次(主席树上的一个点可能被多个节点指向),打标记
int New(){
int x=pool.New();
is_del[x]=0;
return x;
}
void push_up(int x){
tree[x].val=tree[lson(x)].val+tree[rson(x)].val;
}
void update(int &x,int last,int upos,int l,int r){
x=New();
tree[x]=tree[last];
if(l==r){
tree[x].val++;
return;
}
int mid=(l+r)>>1;
if(upos<=mid) update(tree[x].ls,tree[last].ls,upos,l,mid);
else update(tree[x].rs,tree[last].rs,upos,mid+1,r);
push_up(x);
}
int query(int xl,int xr,int ql,int qr,int l,int r){
if(ql<=l&&qr>=r){
return tree[xr].val-tree[xl].val;
}
int mid=(l+r)>>1;
int ans=0;
if(ql<=mid) ans+=query(tree[xl].ls,tree[xr].ls,ql,qr,l,mid);
if(qr>mid) ans+=query(tree[xl].rs,tree[xr].rs,ql,qr,mid+1,r);
return ans;
}
void del(int x){
if(is_del[x]) return;
is_del[x]=1;
pool.del(x);
if(tree[x].ls) del(tree[x].ls);
if(tree[x].rs) del(tree[x].rs);
tree[x].val=tree[x].ls=tree[x].rs=0;
}
}T;
int top=0;
vector< pair<int,int> >v[maxlogn+5];//存储每个组里的修改操作
int sz[maxlogn+5];
void rebuild(int id){//重构某个组
for(int i=1;i<=sz[id];i++){
//把第i个y坐标更新
T.update(root[id][i],root[id][i-1],v[id][i-1].second,1,maxn*2);
}
}
void insert(pair<int,int> p){
sz[++top]=1;
v[top].push_back(p);
rebuild(top);
while(top>1&&sz[top-1]==sz[top]){//类似二进制加法进位,去合并
for(int i=0;i<sz[top];i++) v[top-1].push_back(v[top][i]);
sort(v[top-1].begin(),v[top-1].end());//排序,相当于离散化
v[top].clear();
for(int i=1;i<=sz[top];i++){
if(root[top-1][i]) T.del(root[top-1][i]);//先删除,等下再暴力重构
if(root[top][i]) T.del(root[top][i]);
}
sz[top-1]+=sz[top];
rebuild(top-1);
top--;
}
}
int query(pair<int,int>p,int k){
int x=p.first,y=p.second,l,r;
int ans=0;
for(int i=1;i<=top;i++){
l=upper_bound(v[i].begin(),v[i].end(),make_pair(x-k,0))-v[i].begin();
r=upper_bound(v[i].begin(),v[i].end(),make_pair(x+k,INF))-v[i].begin();
if(l<=r) ans+=T.query(root[i][l],root[i][r],max(y-k,0),min(y+k,maxn*2),1,maxn*2);
}
return ans;
}
char s[10];
int main(){
int x,k;
scanf("%d %d",&n,&q);
pool.ini(maxn*2*maxlogn);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
insert(make_pair(a[i]+i,a[i]-i+maxn));//为了防止负数,要+maxn,注意数组要开两倍
}
for(int i=1;i<=q;i++){
scanf("%s",s);
if(s[0]=='M'){
scanf("%d %d",&x,&k);
a[x]=k;
insert(make_pair(a[x]+x,a[x]-x+maxn));
}else{
scanf("%d %d",&x,&k);
printf("%d\n",query(make_pair(a[x]+x,a[x]-x+maxn),k));
}
}
}
[BZOJ 2989]数列(二进制分组+主席树)的更多相关文章
- bzoj2989&&4170数列——二进制分组+主席树
题意的转化挺巧妙的 可以联想到曼哈顿距离! 并且,所谓的修改还要查询历史版本,并且修改之间不动只算一次,不就是给平面上加一个点吗? 看成(x,a[x])的点 就是一个菱形区域 转切比雪夫距离,变成矩形 ...
- 2019.01.21 bzoj2989: 数列(二进制分组+主席树)
传送门 二进制分组入门题. 主席树写错调题2h+2h+2h+体验极差. 题意简述:给一堆点,支持加入一个点,询问有多少个点跟(x,y)(x,y)(x,y)曼哈顿距离不超过kkk. 思路:题目要求的是对 ...
- [BZOJ 2989]数列(CDQ 分治+曼哈顿距离与切比雪夫距离的转化)
[BZOJ 2989]数列(CDQ 分治) 题面 给定一个长度为n的正整数数列a[i]. 定义2个位置的graze值为两者位置差与数值差的和,即graze(x,y)=|x-y|+|a[x]-a[y]| ...
- Bzoj 1901: Zju2112 Dynamic Rankings 主席树,可持久,树状数组,离散化
1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 6321 Solved: 2628[Su ...
- BZOJ.5319.[JSOI2018]军训列队(主席树)
LOJ BZOJ 洛谷 看错了,果然不是\(ZJOI\)..\(jry\)给\(JSOI\)出这么水的题做T3么= = 感觉说的有点乱,不要看我写的惹=-= 对于询问\(l,r,k\),设\(t=r- ...
- BZOJ 2989: 数列/4170: 极光
题解: n倍经验题 首先比较容易想到的是对绝对值分类讨论 然后是4维偏序 1.查询和修改顺序 2.x>y 3.a[x]>a[y] 4.(x+a[x])-(y+a[y])<=k 这样是 ...
- BZOJ 2006 超级钢琴(堆+主席树)
很好的一道题. 题意:给出长度为n的数列,选择k个互不相同的区间,满足每个区间长度在[L,R]内,求所有选择的区间和的总和最大是多少.(n,k<=5e5). 首先将区间和转化为前缀和之差,那么我 ...
- BZOJ 2223 [Coci 2009]PATULJCI | 主席树练习 (好像是个权限题啊)
题目: 给个序列,问[l,r]区间内是否存在x>(r-l+1)>>1 题解: 好像大家都觉得这个题比较简单,没人写题解啊 先说BZOJ样例的格式应该是,第二个数是序列中数的范围(就是 ...
- bzoj 4448 [Scoi2015]情报传递(主席树,LCA)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4448 [题意] 给定一颗树,询问一条路径上权值小于t-c的点数. [思路] 将一个2查 ...
随机推荐
- 用户界面控件Telerik UI for WinForms发布R2 2019|附下载
Telerik UI for WinForms拥有适用Windows Forms的110多个令人惊叹的UI控件.所有的UI for WinForms控件都具有完整的主题支持,可以轻松地帮助开发人员在桌 ...
- 如何将上个SQL的结果作为参数传递给下个SQL
如何将上个SQL的结果作为参数传递给下个SQL: ##source认证 kinit认证 source /home/omm/ficlient/bigdata_env kinit -k -t /ETL/c ...
- Hive 笔试题
Hive 笔试题 考试时间: 姓名:____________ 考试成绩:____________ 考试时长:180 分钟 注意事项: 1. 自主答题,不能参考任何除本试卷外的其它资料. 2. 总成绩共 ...
- 【前端】CSS基础
3种html中设置css的方法: head中style标签 head中link标签rel属性.href后跟css文件目录 在需要使用css样式的标签添加style属性 1.CSS选择器 #id值 .c ...
- pyqt5-QPlainTextEdit普通文本
继承:QAbstractScrollArea QPlainText和QTextEdit大致功能实现差不多,不能有图片.框架.表格等 QPlainText是安行滚动的,QTextEdit是安像素滚动的 ...
- 利用BeautifulSoup爬去我爱我家的租房数据
因为之前对BeautifulSoup一直不是很熟悉,刚好身边的朋友同事在找房子,就想着能不能自己写个爬虫爬一下数据,因此就写了这个爬虫.基本都是边看书边写的,不过也没什么好讲的.直接粘代码了. # c ...
- makefile filter &&filter-out
sources := foo.c bar.c baz.s abc.h foo: $(sources) gcc $(filter %.c %.s,$(sources)) -o ...
- 好用的jQuery分页插件
插件源代码: (function ($) { $.fn.extend({ smartpaginator: function (options) { var settings = $.extend({ ...
- 微信小程序自定义底部导航栏组件+跳转
微信小程序本来封装有底部导航栏,但对于想自定义样式和方法的开发者来说,这并不是很好. 参考链接:https://github.com/ljybill/miniprogram-utils/tree/ma ...
- 170905-MyBatis中的关系映射
===关系映射=== 参考文档复习:1对1,1对多,多对多 1.映射(多)对一.(一)对一的关联关系 1).使用列的别名 ①.若不关联数据表,则可以得到关联对象的id属性 ②.若还希望得到关联对象的其 ...