树状数组复习
树状数组复习
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;
}
---- suffer now and live the rest of your life as a champion ----