给定三个整数数组
A=[A1,A2,…AN] , B=[B1,B2,…BN], C=[C1,C2,…CN]
,
请你统计有多少个三元组 (i,j,k)
满足:
1≤i,j,k≤N
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 maxn=100005;
int a[maxn],b[maxn],c[maxn];
int main()
{
int n;
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+1+n);
sort(b+1,b+1+n);
sort(c+1,c+1+n);
long long ans=0;
int cnt1,cnt2;
for(int i=1;i<=n;i++)
{
int l=1;
int r=n;
int mid;
while(l<=r)
{
mid=(l+r)/2;
if(a[mid]<b[i])l=mid+1;
else r=mid-1;
}
cnt1=r;
l=1;
r=n;
while(l<=r)
{
mid=(l+r)/2;
if(c[mid]>b[i])r=mid-1;
else l=mid+1;
}
cnt2=n-l+1;
ans+=(long long)cnt1*cnt2;
}
cout<<ans<<endl;
}
这是STL的二分法
关于lower_bound和upper_bound的用法
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long lld;
const int N = 100005;
int a[N], b[N], c[N];
int n;
lld sum;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &b[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &c[i]);
//由于二分的前提是单调序列 所以预先对a b c排序 直接sort
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + n);
sort(c + 1, c + 1 + n);
for (int i = 1; i <= n; i++)
{
//直接用STL中的两个二分函数解决
lld x = (lower_bound(a + 1, a + 1 + n, b[i]) - a) - 1; //在数组a中找比b[i]小的数
lld y = n - (upper_bound(c + 1, c + 1 + n, b[i]) - c) + 1; //在数组c中找比b[i]大的数
sum += x * y;
}
printf("%lld", sum);
return 0;
}
前缀和的高级写法
#include<iostream>
#include<cstring>
#include<algorithm>
#define N 100010
using namespace std;
typedef long long LL;//防止越界
int n;
int a[N],b[N],c[N];
int as[N];//表示在a中有多少个数,小于b[i]
int cs[N];//表示在c中有多少个数,大于b[i]
int cnt[N],s[N];//模拟
int main()
{
cin>>n;
//因为要预处理as,cs数组所以要保证a,b,c数组向后偏移防止越界
for(int i = 0;i < n;i ++) scanf("%d",&a[i]),a[i]++;
for(int i = 0;i < n;i ++) scanf("%d",&b[i]),b[i]++;
for(int i = 0;i < n;i ++) scanf("%d",&c[i]),c[i]++;
//处理as数组
for(int i = 0;i < n;i ++) cnt[a[i]] ++;//桶插法
for(int i = 1;i < N;i ++) s[i] = s[i-1] + cnt[i];//按顺序处理前缀和
for(int i = 0;i < n;i ++) as[i] = s[b[i] - 1];
//处理前缀和之后,要统计归位需要还原(加-1),拿出在s里面所有小于b[i]的总和
//回收再利用
memset(cnt,0,sizeof cnt);
memset(s,0,sizeof s);
//处理cs数组
for(int i = 0;i < n;i ++) cnt[c[i]] ++;
for(int i = 1;i < N;i ++) s[i] = s[i-1] + cnt[i];//因为要整理c数组大于b的值最大不能超过N
for(int i = 0;i < n;i ++) cs[i] = s[N-1] - s[b[i]];
//N的前缀和-b数组值的前缀和=c数组小于b[i]的个数总和
//累加所有三元组情况
LL res = 0;
for(int i = 0;i < n;i ++) res += (LL)as[i]*cs[i];
cout<<res<<endl;
return 0;
}