题目链接:点击打开链接
树状数组,每个区间保存此区间中有多少未取过的数,每次输入k,查区间内刚好是k个数的点,然后update(k,-1)。
对区间更新对点查询,对区间(a,b)加v操作:update(a,v),update(b,-v),对x点查询:正常quary(x)。
本题每次都更新至区间结尾,所以不需要update(b,-v)。
当需要确定第一个达到某值的点时,可使用:
int quary(int n){
int cur=0;
int k=0;
for(int i=18;i>=0;i--){
if(k+(1<<i)<=N&&cur+c[k+(1<<i)]<n){
k+=(1<<i);
cur+=c[k];
}
}
return k+1;
}
cur+c[k+(1<<i)]<n)此处必须为小于而不是小于等于,因为得到的是应是第一个达到某值的,而算法是从大到小判定的,结果位置应是最接近某值的数+1。
结果用long long。。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define FOR(i, x, y) for(int i = x; i <= y; i++)
#define MS(x, y) memset(x, y, sizeof(x))
using namespace std;
int c[277777];
int N,T,K;
int lowbit(int n){
return (n&(-n));
}
void update(int n,int v){
while(n<=N){
c[n]+=v;
n+=lowbit(n);
}
}
int quary(int n){
int cur=0;
int k=0;
for(int i=18;i>=0;i--){
if(k+(1<<i)<=N&&cur+c[k+(1<<i)]<n){
k+=(1<<i);
cur+=c[k];
}
}
return k+1;
}
int main(){
int T;
cin>>T;
int cas=0;
while(T--){
scanf("%d%d",&N,&K);
FOR(i,0,N){
c[i]=0;
}
FOR(i,1,N){
update(i,1);
}
int t;
long long res=0;
FOR(i,1,K){
scanf("%d",&t);
int k=quary(t);
res+=k;
update(k,-1);
}
cout <<"Case "<<++cas<<": "<<res<<endl;
}
return 0;
}
本文介绍如何使用树状数组进行区间更新和查询,特别适用于每次更新至区间结尾的情况。详细阐述了算法实现和应用实例。
497

被折叠的 条评论
为什么被折叠?



