第九届蓝桥杯【C++省赛B组】【第六题:递增三元组】——二分解法或前缀和(附解题代码)

这篇博客介绍了如何利用二分查找算法高效地解决一个计数问题:给定三个整数数组A、B、C,统计满足A[i] < B[j] < C[k]条件的三元组(i, j, k)的数量。博主提供了三种不同的二分查找实现方式,包括库函数和手写二分,并给出了对应的输入输出样例。这些方法对于大规模数据具有较好的时间复杂度,适合处理范围在1到10^5之间的整数数组。

给定三个整数数组

A=[A1,A2,…AN],
B=[B1,B2,…BN],
C=[C1,C2,…CN],

请你统计有多少个三元组 (i,j,k) 满足:
1)1≤i,j,k≤N
2)Ai<Bj<Ck

输入格式
第一行包含一个整数 N。

第二行包含 N 个整数 A1,A2,…AN。

第三行包含 N 个整数 B1,B2,…BN。

第四行包含 N 个整数 C1,C2,…CN。

输出格式
一个整数表示答案。

数据范围
1≤N≤105,
0≤Ai,Bi,Ci≤105
输入样例:
3
1 1 1
2 2 2
3 3 3
输出样例:
27

库函数二分

#include <iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int a[N],b[N],c[N];
int main()
{
  int n;
  long long res = 0;
  cin>>n;
  for(int i = 0; i < n; i++) scanf("%d",&a[i]);
  for(int i = 0; i < n; i++) scanf("%d",&b[i]);
  for(int i = 0; i < n; i++) scanf("%d",&c[i]);
  sort(a,a+n); sort(b,b+n); sort(c,c+n);
  for(int i = 0; i < n; i++){
  	int j = lower_bound(a,a+n,b[i])-a;
	  int k = upper_bound(c,c+n,b[i])-c;
	  res = res + 1ll*j*(n-k);
  }
  cout<<res<<endl;
  return 0;
}

手写二分做法(1):

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int a[N],b[N],c[N];
int lower(int *p,int l,int r,int x){//找出第一个大于或等于x的元素位置 
	/* 
	在 1 2 3 4 6 8 9中lower 当 x = 5 返回的是 6的位置
	推导:当l=r-1时,mid = (r-1+r)>>1 = r-1 = l,所以p[mid(l)]<x 推导出l = mid+1 = r。  
	
	在 1 2 3 4 5 5 5 5 6 8 9中upper 当 x = 5 返回的是最左边5的位置(因为当等于x时是r在移动) 
	*/
	int n = r;
	if(p[n]<x) return n+1; //若在最右边的数p[n]仍然小于x,即整个序列都应是小于x的。 
	if(p[l]>=x) return 0; // 不存在小于x的数 
	while(l<r){ //整数二分 
		int mid = l+r>>1;
		if(p[mid] >= x) r = mid;
		else l = mid+1;
	}
	return l; //获得的是第一个大于等于x的数的位置 
}

int upper(int *p,int l,int r,int x){//找出第一个大于x的元素位置 
	/*
	在 1 2 3 4 6 8 9中upper 当 x = 5 返回的是4的位置 
	推导:当l=r-1时,mid = (r-1+r+1)>>1 = r,所以p[mid(r)]>x 推导出r = mid-1 = l。  
	
	在 1 2 3 4 5 5 5 5 6 8 9中upper 当 x = 5 返回的是最右边5的位置(因为当等于x时是l在移动)
	*/
	int n = r;
	if(p[l] > x) return 0;  //若在最左边的数p[l]大于x,即整个序列都应是大于x的。
	if(p[n] <= x) return n+1; //不存在大于x的数 
	while(l<r){ //整数二分 
		int mid = l+r+1>>1;
		if(p[mid] > x) r = mid - 1;
		else l = mid;
	}
	
	return l+1; //获得的是第一个大于x的数的位置 
}

int main(){
	int n;
	long long res=0;
	cin>>n;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int i=0;i<n;i++) cin>>b[i];
	for(int i=0;i<n;i++) cin>>c[i];
	sort(a,a+n); sort(b,b+n); sort(c,c+n);
	for(int i = 0; i < n; i++){
		int x = lower(a,0,n-1,b[i]);  //在有序序列中找出第一个大于或等于b[i]的元素位置 
		int y = upper(c,0,n-1,b[i]); //在有序序列中找出第一个大于b[i]的元素位置 
		res += 1LL*x*(n-y); //n-1-y+1 = n-y 
	}
	cout<<res;
	return 0;
}

手写二分做法(2):

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int a[N],b[N],c[N];
int lower(int x,int n){
    int l = 0, r = n;
    while(l<r){
        int mid = l + r + 1 >> 1;
        if(a[mid] > x) r = mid - 1;
        else l = mid;
    }
    while(a[l] == x && l > 0) l--;
    return l;
}
int uper(int x,int n){
    int l = 0, r = n;
    while(l<r){
        int mid = l + r >> 1;
        if(c[mid] > x) r = mid;
        else l = mid + 1;
    }
    if(c[l] <= x) return n+1;
    while(c[l] == x && l < n + 1) l++;
    return l;
}
int  main(){
    int n;
    long long res = 0;
    cin>>n;
    for(int i = 1; i <= n; i++ ) cin>>a[i];
    for(int i = 1; i <= n; i++ ) cin>>b[i];
    for(int i = 1; i <= n; i++ ) cin>>c[i];
    sort(a+1,a+n+1); sort(b+1,b+n+1); sort(c+1,c+n+1);
    for(int i = 1; i <= n; i++ ){
        int x = lower(b[i],n); //获得最大小于b[i]的位置
        int y = uper(b[i],n); //获得最小大于b[i]的位置
        // cout<<x<<" "<<y<<endl;
        res += 1LL * x * (n-y+1);
    }
    cout<<res;
    return 0;
}

前缀和做法:

#include<iostream>
using namespace std;
const int N = 100010;
int a[N],b[N],c[N],sa[N],sc[N],cnta[N],cntc[N];
int main(){
    int n;
    long long ans = 0;
    cin>>n;
    for(int i = 1; i <= n; i++ ){
        cin>>a[i];
        cnta[a[i]]++;
    }
    sa[0] = cnta[0];
    for(int i = 1; i < N; i++ ) sa[i] = sa[i-1] + cnta[i];
    for(int i = 1; i <= n; i++ ) cin>>b[i];
    int maxn = 0;
    for(int i = 1; i <= n; i++ ){
        cin>>c[i];
        cntc[c[i]]++;
    }
    sc[0] = cntc[0];
    for(int i = 1; i < N; i++ ) sc[i] = sc[i-1] + cntc[i];
    for(int i = 1; i <= n; i++ ){
        ans += 1LL * sa[b[i]-1] * (sc[N-1] - sc[b[i]]);
    }
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值