ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树状数组

2022-07-23 22:04:11  阅读:136  来源: 互联网

标签:ch 树状 int 线段 maxn 数组


\(emm……\),作为一个完全可以被线段树代替的数据结构,其主要优点只有代码短与常数小
然而总有无聊的出题人卡常以及类似我的蒟蒻调不出线段树,所以还是得学一下的

树状数组天然用来维护前缀和,所以支持区间修改,单点查询;单点修改,区间查询
如果非要区间修改,区间修改也不是不行:
首先差分,令 \(d_i=a_i-a_{i-1}\)
\(S_i=\sum_{j=1}^{i}a_j\)
\(=\sum_{j=1}^i d_j(i-j+1)\)
\(=(i+1)\sum_{j=1}^{i}d_j-\sum_{j=1}^{i}jd_j\)
然后开两个树状数组维护 \(d_j\) 和 \(jd_j\) 即可

对于二维树状数组,直接开二维即可
注意内层循环的 \(y\) 不能直接用传进来的参数,应该每次循环都重新定义

关于树状数组上的二分,抄袭 \(cyh\) 代码(附赠改良码风服务

int kth(int k,int x=0){ // 查询第k大
    for(int i=log2(n);i>=0;i--)if(x<n&&k>c[x+(1<<i)])k-=c[x],x+=1<<i;
    return x+1;
}

一个小小小 \(trick\) 是假如更新是单点插入,查询是前缀 \(max\),那么可以直接写成插入时 \(x-=low\),查询时 \(x+=low\) 即可


分块套树状数组

相当于是一个二维树状数组,只不过把第一维开在了块上,这样就可以在时空复杂度正确的情况下维护信息了
但是注意此时复杂度的保证是点是离散的,保证一个横坐标对应的点不会很多,这样零散块才可以很方便地暴力
复杂度好像比较神奇,是 \(O(\sqrt n+log^2n)\),因为分块的根号是跟在树状数组的 \(log\) 后面的,因此不会有 \(log\) 乘根号这一项,在许多情境下运行效率大大优于树套树


CF1093E Intersection of Permutations

可以发现这道题的点是一个排列,于是满足了使用条件
注意由于树状数组是前缀和型的,因此询问应当拆分成容斥形式

代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
const int maxm=505;
int n,m,re[maxn],_y[maxn],op,x,y,xx,yy,sum[maxm][maxn],num,siz,l[maxn],r[maxn],c[maxn];
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void add(int pos,int w,int op){
	for(int i=c[pos];i<=num;i+=i&-i)
		for(int j=w;j<=n;j+=j&-j)
			sum[i][j]+=op;
	return ;
}
void change(int x,int y){
	add(x,_y[x],-1),add(y,_y[y],-1);
	swap(_y[x],_y[y]);
	add(x,_y[x],1),add(y,_y[y],1);
	return ;
}
int ask(int pos,int w){
	if(!pos)return 0;int res=0;
	for(int i=l[c[pos]];i<=pos;i++)res+=_y[i]<=w;
	for(int i=c[pos]-1;i;i-=i&-i)
		for(int j=w;j;j-=j&-j)
			res+=sum[i][j];
	return res;
}
int ask(int y,int yy,int x,int xx){
	return ask(xx,yy)-ask(xx,y-1)-ask(x-1,yy)+ask(x-1,y-1);
}
int main(){
	n=read();m=read();for(int i=1,x;i<=n;i++)x=read(),re[x]=i;
	for(int i=1,x;i<=n;i++)x=read(),_y[i]=re[x];
	siz=sqrt(n),num=(n-1)/siz+1;
	for(int i=1;i<=n;i++)c[i]=(i-1)/siz+1;
	for(int i=1;i<=num;i++)l[i]=r[i-1]+1,r[i]=min(n,siz*i);
	for(int i=1;i<=n;i++)add(i,_y[i],1);
	while(m--){
		op=read();x=read(),y=read();
		if(op==1){
			xx=read(),yy=read();
			printf("%d\n",ask(x,y,xx,yy));
		}
		else change(x,y);
	}
	return 0;
}

树状数组套主席树

可以用于求解区间第 \(k\) 大,如果不需离散化可以在线进行
类似于线段树套平衡树,只不过把线段树替换为更快的树状数组,而平衡树+二分的过程变为了权值线段树上的二分
只需要记录出这一过程中树状数组上所有树根是什么,线段树的二分工作判断时应当统计当前所有树根的信息然后进行判断,时间复杂度为 \(log^2n\),效率较高

标签:ch,树状,int,线段,maxn,数组
来源: https://www.cnblogs.com/yang-cx/p/15553460.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有