用法及操作
单点修改+区间查询
得到c对a的管理数
int lowbit(int x) {
//算出x二进制的从右往左出现第一个1以及这个1之后的那些0组成数的二进制对应的十进制的数
return x & -x;
}
单点修改
//相减直接将k变为-k
void add(int x, int k) {
while (x <= n) { //不能越界
c[x] = c[x] + k;
x = x + lowbit(x);
}
}
1~x的和
int getsum(int x) { // a[1]……a[x]的和
int ans = 0;
while (x >= 1) {
ans = ans + c[x];
x = x - lowbit(x);
}
return ans;
}
完整代码
int n;
const int maxn=1e6+5;
int c[maxn];
int lowbit(int x) {
return x&-x;
}
void add(int x, int k) {
while (x<=n) {
c[x]=c[x]+k;
x=x+lowbit(x);
}
}
int getsum(int x) {
int ans=0;
while (x>=1) {
ans=ans+c[x];
x=x-lowbit(x);
}
return ans;
}
int query(int l, int r) {
return getsum(r)-getsum(l-1);
}
void main(){
ios::sync_with_stdio(false);
cin>>n;
int a;
for (int i=1; i<=n; i++) {
cin>>a;
add(i,a);
}
/.........../
}
区间修改+单点查询
void nadd(int l, int r, int x){
add(l,x),add(r+1,-x);
}
int query(int x){
int res=0;
while (x) {
res+=c[x];
x-=lowbit(x);
}
return res;
}
void main(){
/............./
for (int i=1; i<=n; i++){
cin>>a[i];
add(i,a[i]-a[i-1]);
}
/............./
}
另一种写法
void add(ll p, ll x){
for(int i = p; i <= n; i += i & -i)
sum1[i] += x, sum2[i] += x * p;
}
void range_add(ll l, ll r, ll x){
add(l, x), add(r + 1, -x);
}
ll ask(ll p){
ll res = 0;
for(int i = p; i>0; i -= i & -i)
res += (p + 1) * sum1[i] - sum2[i];
return res;
}
ll range_ask(ll l, ll r){
return ask(r) - ask(l - 1);
}
区间修改+区间查询
const int maxn=1e6;
int t1[maxn],t2[maxn];
int n;
void add(int x, int k){
int tmp=x*k;
while (x<=n){
t1[x]+=k;
t2[x]+=tmp;
x+=lowbit(x);
}
}
void nadd(int l,int r,int x){
add(l,x),add(r+1,-x);
}
int getsum(int *t, int k) {
int ret = 0;
while (k) {
ret += t[k];
k -= lowbit(k);
}
return ret;
}
ll ngetsum(int l, int r) {
return (r+1ll)*(getsum(t1,r)-getsum(t1,l-1))-(getsum(t2,r)-getsum(t2,l-1));
}
优化
// O(n)建树
void init() {
for (int i = 1; i <= n; ++i) {
t[i] += a[i];
int j = i + lowbit(i);
if (j <= n) t[j] += t[i];
}
}
int kth(int k) { //权值树状数组查询第k小
int cnt = 0, ret = 0;
for (int i = log2(n); ~i; --i) {
ret += 1 << i;
if (ret >= n || cnt + t[ret] >= k)
ret -= 1 << i;
else
cnt += t[ret];
}
return ret + 1;
}
//时间戳优化
int tag[MAXN], t[MAXN], Tag;
void reset() { ++Tag; }
void add(int k, int v) {
while (k <= n) {
if (tag[k] != Tag) t[k] = 0;
t[k] += v, tag[k] = Tag;
k += lowbit(k);
}
}
int getsum(int k) {
int ret = 0;
while (k) {
if (tag[k] == Tag) ret += t[k];
k -= lowbit(k);
}
return ret;
}
应用
求逆序对
对于数组a,我们将其离散化处理为b[].区间查询与单点修改
void update(int p)
{
while(p<=n)
{
a[p] ++;
p+=lowbit(p);
}
}
int getsum(int p)
{
int res = 0;
while(p)
res += a[p],p -= lowbit(p);
return res;
}
for(int i=1;i<=n;i++){
update(b[i]+1);
res += i-getsum(b[i]+1);//res就是a逆序对个数,ask,需注意b[i]应该大于0
}
求区间最大值
void Update(int i,int v)
{
while(i<=maxY)
{
t[i] = max(t[i],v);
i += lowbit(i);
}
}
int query(int i)
{
int ans = 0;
while(i)
{
ans = max(ans,t[i]);
i -= lowbit(i);
}
return ans;
}