树状数组复习

树状数组复习

1. 楼兰图腾: 区间查询

  • 对于 v : 统计左侧有多少个大于x的,右侧有多少个大于x的, 两者相乘
  • 对于 ^ : 统计左侧有多少个小于x的,右侧有多少个小于x的, 两者相乘

对于每一个加入的数,相当于给那个值的位置+1

每一次统计:[1,v] [v,+inf] 个数



#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 2e5 + 5;

int n,temp;
int arr[N];


inline int lowbit(int x){
    return x & -x;
}

inline void add(int pos,int n,int val){
    while(pos <= n){
        arr[pos] += val;
        pos += lowbit(pos);
    }
}

inline int query(int pos){
    int ans = 0;
    while(pos >= 1){
        ans += arr[pos];
        pos -= lowbit(pos);
    }
    return ans;
}




int main(){
    ll ans1 = 0;
    ll ans2 = 0;
    scanf("%d",&n);
    
    
    for(int i = 1; i <= n; ++i){
        scanf("%d",&temp);
        add(temp,n,1);
        ll a = query(temp-1); // 之前有多少个小于的
        ll b = (temp-1) - a;  // 之后有多少个小于的
        
        ll c = query(n) - query(temp); // 之前有多少个大于的
        ll d = (n-temp) - c;    // 之后有多少个大于的
        
        ans2 += a*b;
        ans1 += c*d;
    }
    
    printf("%lld %lld",ans1,ans2);
    return 0;
}

2. 谜一样的牛: 二分+区间查询(前缀动态第k大)

从后向前遍历,不断赋予身高;第i头牛前面有k头牛比它高的时候,表明当前这一头牛是剩余可选身高里第k+1高的牛

需要在[1,n]区间内动态查询第k+1大值;一开始所有数都存在,中途修改某一个值不存在

查询方法: 二分法 + 树状数组区间和

二分右端点,查询第一个区间和为[k+1]的右端点

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5+10;

int arr[N];
int temp[N];

int n;

inline int lowbit(int x){
    return x & -x;
}

inline void add(int pos,int n,int v){ // 某个数+1
    while(pos <= n){
        arr[pos] += v;
        pos += lowbit(pos);
    }
}

inline int query(int pos){ // 获取小于等于某个数的个数
    int ans = 0;
    while(pos >= 1){
        ans += arr[pos];
        pos -= lowbit(pos);
    }
    return ans;
}

inline int find_pos(int val){
    int l = 1, r = n;
    int ans = -1;
    while( l <= r ){
        int mid = (l+r)>>1;
        if(query(mid) >= val){
            ans = mid;
            r = mid-1;
        }else{
            l = mid+1;
        }
    }
    return ans;
}


int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i){
        add(i,n,1);
    }
    
    for(int i = 2; i <= n; ++i){
        scanf("%d",&temp[i]); 
    }
    
    for(int i = n; i >= 1; --i){
        temp[i] = find_pos(1+temp[i]);
        add(temp[i],n,-1);
    }
    int first = 1;
    for(int i = 1; i <= n; ++i){
        if(first)first = 0;else putchar('\n');
        printf("%d",temp[i]);
    }
    return 0;
}
posted @ 2021-01-30 09:46  popozyl  阅读(63)  评论(0编辑  收藏  举报