1.题目引入:
给定三个整数数组
A=[A1,A2,…AN]A=[A1,A2,…AN],
B=[B1,B2,…BN]B=[B1,B2,…BN],
C=[C1,C2,…CN]C=[C1,C2,…CN],请你统计有多少个三元组 (i,j,k)(i,j,k) 满足:
- 1≤i,j,k≤N1≤i,j,k≤N
- Ai<Bj<CkAi<Bj<Ck
输入格式
第一行包含一个整数 NN。
第二行包含 NN 个整数 A1,A2,…ANA1,A2,…AN。
第三行包含 NN 个整数 B1,B2,…BNB1,B2,…BN。
第四行包含 NN 个整数 C1,C2,…CNC1,C2,…CN。
输出格式
一个整数表示答案。
数据范围
1≤N≤1051≤N≤105,
0≤Ai,Bi,Ci≤1050≤Ai,Bi,Ci≤1052.样例输出:
输入样例:
3 1 1 1 2 2 2 3 3 3
输出样例:
27
可以看到这个N=1e5 级别如果使用三层 for 循环进行单个判定一定会爆时间,那如果我们只判定中间那个数组,我们发现只要在 第一个数组找出小于 b[i] 的数乘以最后一个数组中大于b[i] 的数组 最后将这些满足的项相加就是我们要的结果, 那么如何查找满足条件数的数量呢,这里我们主要利用的是前缀和思路它的复杂度是O(n) 这里的n 是数组取值的范围,具体解释都付在代码上了这里不在过多赘述,当然也可以利用 排序+二分以及双指针的思路方法多多(大家有兴趣可以去实践一下)如果大家还有什么好的方法欢迎一起交流学习。
3.代码如下:
#include<iostream> #include<cstring> #include<algorithm> using namespace std; int n; typedef long long ll; const int maxn=1e5+10; int a[maxn],b[maxn],c[maxn]; // as[]表示在 A[]中有多少个数小于b[i] // cs[]表示在c[] 中有多少个数大于b[i] // as[]*cs[]*b[i---n] 表示满足条件的总数 int as[maxn],cs[maxn]; int cnt[maxn],s[maxn]; int main() { scanf("%d",&n); /* 利用前缀和 这里的数据范围从 0 开始 为避免边界特判直接将所有数都进行加一操作不必要的麻烦 例如: s[n]=s[n]-s[0]; // 保证这个s[0]=0 可以省去 */ for(int i=0;i<n;i++) { scanf("%d",&a[i]),a[i]++; } for(int i=0;i<n;i++) cin>>b[i],b[i]++; for(int i=0;i<n;i++) cin>>c[i],c[i]++; for(int i=0;i<n;i++) cnt[a[i]]++; // 将每个数进行标记加一 for(int i=1;i<maxn;i++) s[i]=s[i-1]+cnt[i]; // 计算 i 的前缀和 for(int i=0;i<n;i++) as[i]=s[b[i]-1]; // 注意题目中是严格小 memset(cnt,0,sizeof cnt); memset(s,0,sizeof s); for(int i=0;i<n;i++) cnt[c[i]]++; for(int i=1;i<maxn;i++) s[i]=s[i-1]+cnt[i]; for(int i=0;i<n;i++) cs[i]=s[maxn-1]-s[b[i]]; ll ans=0; for(int i=0;i<n;i++) { ans+=(ll) as[i]*cs[i]; } cout<<ans<<"\n"; return 0; }