线段树
线段树的基本实现
int Sum[MAX];
int A[MAX];
int Add[MAX]; //Add为懒惰标记
void Build(int l,int r,int rt)
{
if(l==r)
{
Sum[rt]=Stu[l];
return;
}
int mid=(l+r)/2;
Build(l,m,2*rt);
Build(m+1,r,2*rt+1);
Sum[rt]=Sum[2*rt]+Sum[2*rt+1];
}
void push_up(int rt)
{
Sum[rt]=Sum[2*rt]+Sum[2*rt+1];
}
void push_down(int rt,int ln,int rn)
{
if(Add[rt])
{
Add[2*rt]+=Add[rt];
Add[2*rt+1]+=Add[rt];
Sum[2*rt]+=Add[rt]*ln;
Sum[2*rt+1]+=Add[rt]*rn;
Add[rt]=0;
}
}
//将A[L]+=C
void UpdatePoint(int L,int C,int l,int r,int rt)//l,r表示当前节点区间
{
if(l==r)
{
Sum[rt]+=C;
return;
}
int mid=(l+r)/2;
//判断在做
if(L<=m)
Update(L,C,l,m,rt*2);
else Update(L,C,m+1,r,rt*2+1);
push_up(rt);
}
//A[L,R]+=C
//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点
void Update(int L,int R,int C,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
Sum[rt]+=C*(r-l+1);//如果本区间完全在操作区间以内
Add[rt]+=C; //增加Add标记,表示本区间的Sum正确,子区间的Sm仍需要根据Add标记
return;
}
int mid=(l+r)/2;
//下推
push_down(rt,mid-l+1,r-mid);
if(L <= m)
Update(L,R,C,l,mid,rt*2);
if(R > m)
Update(L,R,C,mid+1,r,rt*2+1);
push_up(rt);
}
int Query(int L,int R,int l,int r,int rt)
{
//在区间内 直接返回
if(L<=l&&r<=R)
return Sum[rt];
int mid=(l+r)/2;
push_down(rt,mid-l+1,r-mid);
int ans=0;
if(L<=m)
ans+=Query(L,R,l,mid,rt*2);
if(R>m)
ans+=Query(L,R,mid+1,r,rt*2+1);
return ans;
}
树状数组
基本实现
int Bit[MAX_N ];
int sum(int i)
{
int s = 0;
while(i > 0)
{
s += Bit[i];
i -= i & -i;
}
return s;
}
void add(int i, int x)
{
while(i <= n)
{
Bit[i] += x;
i += i & -i;
}
}
树状数组的应用
树状数组求逆序数(连续排列)
#include<iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define MAX_N 100
int bit[MAX_N + 1], n;
int a[MAX_N];
int sum(int i)
{
int s = 0;
while(i > 0)
{
s += bit[i];
i -= i & -i;
}
return s;
}
void add(int i, int x)
{
while(i <= n)
{
bit[i] += x;
i += i & -i;
}
}
void solve()
{
long long ans = 0;
for(int j = 0; j < n; ++j)
{
ans += j - sum(a[j]);
//printf("%d ",ans);
add(a[j], 1);
}
for(int i=1;i<=n;i++)
printf("%lld\n", ans);
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; ++i)
{
scanf("%d", &a[i]);
}
solve();
return 0;
}
线段树的应用
扫描线