目录
树状数组定义
c[i] = a[i-lowbit(i)+1] 到 a[i] 的和 (c[x]其实就是记录了自己到自己的和)
int a[N];
ll c[N];
//查询前缀和
int query(int x){
ll s=0;
for(;x;x-=x&(-x)){
s+=c[x];
}
return s;
}
//修改x位置的加s 差值
void modify(int x,int s){ //a[x]+=s
for(;x<=n;x+=x&(-x)){ //进位之后的数字的c数组都包含了这个修改的数
c[x]+=s; //包含修改的数的都加上s
}
}
树状数组1 (单点修改,区间查询)
题目链接: https://www.luogu.com.cn/problem/P3374
代码:
// Problem: P3374 【模板】树状数组 1
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3374
// Memory Limit: 512 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e6+5;
int a[N];
ll c[N]; //代表的是 a(i-lowbit(i)+1) --> ai 的和
int n,q;
ll query(ll x){
ll s=0;
for(;x;x-=x&(-x)){
s+=c[x];
}
return s;
}
void modify(ll x,ll s){ //c[x]加上s
for(;x<=n;x+=x&(-x)){
c[x]+=s;
}
}
int main(){
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
modify(i,a[i]);
}
for(int i=0;i<q;i++){
ll ty;
cin>>ty;
ll x,d;
cin>>x>>d;
if(ty==1){
modify(x,d);
a[x]=d;
}
else{
cout<<query(d)-query(x-1)<<"\n";
}
}
return 0;
}
树状数组2(区间修改,单点查询)
题目链接: https://www.luogu.com.cn/problem/P3368
思想: 用差分数组,查询单点就是查询差分数组d的前缀和
代码:
// Problem: P3368 【模板】树状数组 2
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3368
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e6+5;
ll a[N];
ll c[N];
int n,q;
ll query(ll x){
ll s=0;
for(;x;x-=x&(-x)){
s+=c[x];
}
return s;
}
void modify(ll x,ll s){
for(;x<=n;x+=x&(-x)){
c[x]+=s;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>q;
ll ls=0;
for(int i=1;i<=n;i++){
cin>>a[i];
modify(i,a[i]-ls);
ls=a[i];
}
for(int i=0;i<q;i++){
ll ty;
cin>>ty;
if(ty==1){
ll x,y,d;
cin>>x>>y>>d;
modify(x,d);
modify(y+1,-d);
}
else{
int x;
cin>>x;
cout<<query(x)<<"\n";
}
}
return 0;
}
逆序对
题目链接: http://oj.daimayuan.top/course/15/problem/653
思想:离散化排序,离散化就是另开一个数组,d, d[i]用来存放第i大的数在原序列的什么位置,比如原序列a={5,3,4,2,1},第一大就是5,他在a中的位是1,所以d[1]=1,同理d[2]=3,········所以d数组为{1,3,2,4,5},
关键是要理解那句标了红色的那句话,不是前缀和,而是有几个数已经存在,假如a[8]等于4,那么就表示[1,8]中只有4个数存在。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+5;
int a[N];
int c[N],d[N];
int n;
//查询
int query(int x){
int s=0;
for(;x;x-=x&(-x)){
s+=c[x];
}
return s;
}
//修改
void modify(int x,int s){
for(;x<=n;x+=x&(-x)){
c[x]+=s;
}
}
bool cmp(int x,int y){
if(a[x]==a[y]) return x>y;
return a[x]>a[y];
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
d[i]=i;
}
sort(d+1,d+n+1,cmp);
int ans=0;
for(int i=1;i<=n;i++){
ans+=query(d[i]-1);
modify(d[i],1);
}
cout<<ans<<"\n";
return 0;
}
树状数组3(区间修改,区间查询)
题目链接: http://oj.daimayuan.top/course/15/problem/635
思想: 差分数组 ,维护 i 和 b[i] 的前缀和
原数组:
a1=b1
a2=b1+b2
a3=b1+b2+b3
所以 区间查询s3=3*b1+2*b2+3*b3
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef unsigned long long u64;
const int N = 2e5+5;
int n,q;
template<class T>
struct BIT{
T c[N];
//查询
T query(int x){
T s=0;
for(;x;x-=x&(-x)){
s+=c[x];
}
return s;
}
//修改
void modify(int x,T s){
//x!=0
for(;x<=n;x+=x&(-x)){
c[x]+=s;
}
}
};
BIT<u64> c1,c2;
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>q;
for(int i=1;i<=q;i++){
int ty;
cin>>ty;
if(ty==1){
int l,r;
u64 d;
cin>>l>>r>>d;
c1.modify(l,d);
c1.modify(r+1,-d);
c2.modify(l,l*d);
c2.modify(r+1,(r+1)*(-d));
}
else{
int x;
cin>>x;
u64 ans=(x+1)*c1.query(x)-c2.query(x);
cout<<ans<<"\n";
}
}
return 0;
}