例题一:求平均值大于等于k的区间个数
之前我也遇到这类题,当时数据范围比较小,n<=1e4,k<=100,甚至不用离散化,当时我的做法是BIT(树状数组)。
当时我是这么想的
1、遇到平均数,各项减去k,前缀和大于等于0的区间就符合题意。
2、从1遍历到n,到i时,每次找之后前缀和大于0的个数,就是以i为左端点时的贡献
3、对于第i个前缀和时,与第i-1位的前缀和相比,i-1之后所有数都减去a[i-1],所以转换为,前缀和不变情况下,每次找大于等于s[i-1]的数
现在我再次遇到这题,用离散化BIT也能过,后来我发现,一个区间[i,j]符合条件,s[j]-s[i-1]>=0,离散化后,各个数变成1到n之间,问题转换为找各个数之后比它大的数的个数。
这是什么?不就是求逆序数吗,难怪我刷cdq分治的题时,会感觉这题是BIT。
反过来想,这题如果用cdq分治,就类似归并排序的模板。
————BIT写法(就是一个加了离散化的求逆序数)、分治写法不写了
const ll inf=1e18+7;
const int maxn=2e5+7;
ll n,m,a[maxn],t[maxn<<2],sum,k,s[maxn],c[maxn],cnt;
ll lowbit(ll i){ return i&(-i); }
void add(ll i){ for(;i<=cnt;i+=lowbit(i)) t[i]++; }
void sub(ll i){ for(;i<=cnt;i+=lowbit(i)) t[i]--; }
ll query(ll i){
int ans=0;
for(;i;i-=lowbit(i)) ans+=t[i];
return ans;
}
int main(){
n=read(); m=read();
for(int i=1;i<=n;i++){
a[i]=read()-m;
s[i]=s[i-1]+a[i];
c[cnt++]=s[i];
}
c[cnt++]=inf; c[cnt++]=0;
sort(c,c+cnt);
cnt=unique(c,c+cnt)-c;
for(int i=1;i<=n;i++){
ll tmp=lower_bound(c,c+cnt,s[i])-c+1;
add(tmp);
}
for(int i=1;i<=n;i++){
ll l=lower_bound(c,c+cnt,s[i-1])-c+1;
ll r=lower_bound(c,c+cnt,s[i])-c+1;
sum+=n-i+1-query(l-1);
sub(r);
}
cout<<sum;
}
例题二:求区间和在[L,R]的区间个数
如果用BIT,和上面代码类似,这是我一开始的代码,过了。
const ll inf=1e18+7;
const int maxn=2e5+7;
ll n,m,a[maxn],t[maxn<<2],sum,k,s[maxn],c[maxn<<2],cnt,l,r;
ll lowbit(ll i){ return i&(-i); }
void add(ll i){ for(;i<=cnt;i+=lowbit(i)) t[i]++; }
void sub(ll i){ for(;i<=cnt;i+=lowbit(i)) t[i]--; }
ll query(ll i){
int ans=0;
for(;i;i-=lowbit(i)) ans+=t[i];
return ans;
}
int main(){
n=read(); l=read(); r=read();
for(int i=1;i<=n;i++){
a[i]=read();
s[i]=s[i-1]+a[i];
c[cnt++]=s[i];
c[cnt++]=s[i]+l-1;
c[cnt++]=s[i]+r;
}
c[cnt++]=inf; c[cnt++]=0; c[cnt++]=l-1; c[cnt++]=r;
sort(c,c+cnt);
cnt=unique(c,c+cnt)-c;
for(int i=1;i<=n;i++){
ll tmp=lower_bound(c,c+cnt,s[i])-c+1;
add(tmp);
}
for(int i=1;i<=n;i++){
ll p=lower_bound(c,c+cnt,s[i-1]+l-1)-c+1;
ll q=lower_bound(c,c+cnt,s[i-1]+r)-c+1;
sum+=query(q)-query(p);
ll tmp=lower_bound(c,c+cnt,s[i])-c+1;
sub(tmp);
}
cout<<sum;
}
既然是刷cdq分治,补一下cdq分治的代码吧。
const int maxn=2e5+7;
ll n,p,q,s[maxn],t,sum;
void cdq(int l,int r){
if(l==r) return;
int mid=l+r>>1;
cdq(l,mid); cdq(mid+1,r);
int x=l,y=l-1;
for(int i=mid+1;i<=r;i++){
while(y+1<=mid&&s[i]>=s[y+1]+p) y++;
while(x<=mid&&s[i]>s[x]+q) x++;
sum+=y-x+1;
}
sort(s+l,s+r+1);
}
int main(){
n=read(); p=read(); q=read();
for(int i=1;i<=n;i++) s[i]=s[i-1]+(t=read());
cdq(0,n);
cout<<sum;
}