目录
思路:
如果用数组暴力的话,每次都要删除操作,复杂度过高。
树状数组比较喜欢和前缀和相联系运用。比如逆序对。先对每一个最深层结点复为1,这样find(x)就可以得到x值。如果某个点已被删除,就标记为0,也就是对其-1。这样查询时,如果在该删除值的前面,无影响。在后面,这样第一个肯定就是。
注意:如果一个数的前一个数和后一个数都已经被删除,这时候第一个和为x的才是,后者也为x因为该点为0,加于不加都一样。这就是需要左侧边界的二分搜索;
具体见:https://www.cnblogs.com/kyoner/p/11080078.html
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<queue>
#include<map>
#include<cstdio>
#include<cmath>
#include<stdlib.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6+50;
ll tree[maxn];
int n,m;
inline int lowbit(int x){
return x&(-x);
}
void add(int x,int v){
for(;x<=n;x+=lowbit(x)) tree[x] += v;
}
ll find(int x){
ll ans = 0;
for(;x>0;x-=lowbit(x)) ans += tree[x];
return ans;
}
int get_k(int x){
int l = 1,r = n;
int mid,t;
while(l <= r){
mid = (l+r)>>1;
t=find(mid);
if(t > x){
r=mid-1;
}
else if(t < x) l=mid+1;
else{
r=mid;
if(l==r) break;
}
}
return r;
}
int main(){
cin.tie(0);cout.tie(0);
int t;
cin >> t;
int _case = 0;
while(t--){
memset(tree,0,sizeof(tree));
cin >> n >> m;
for(int i = 1;i <= n;i++) add(i,1);
ll k;
ll ans = 0;
while(m--){
cin >> k;
ll rec = get_k(k);
ans += rec;
add(rec,-1);
}
printf("Case %d: %lld\n",++_case,ans);
}
return 0;
}