直接枚举肯定会超时,只能采取优化,于是采取了二分查找或者桶+前缀和的办法
二分查找
要减少枚举次数,应该是枚举b数组,然后分别查找a,c数组中的元素
- 对于a数组而言,查找最后一个比b[i]小的位置==第一个b[i]的位置
- 对于b数组而言,查找第一个比b[i]大的位置==最后一个b[i]的位置+1
为什么a数组第一个b[i]位置不用加一?
因为数组下标从0开始,当遇到第一个b[i]位置j,说明前面有j个比b[i]小的位置
为什么c数组最后一个b[i]位置要加一?
因为数组下标从0开始,当遇到最后一个b[i]位置j,说明前面有j+1个<=b[i]的元素
而后面有n-j-1个比b[i]大的元素
ac代码如下
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int a[N];
int b[N];
int c[N];
//严格递增
int n;
int left_search(int x)
{//a[i]中所有比x小的数字,找第一个x
int l = 0, r = n;
while (l < r)
{
int mid = l + r >> 1;
if (a[mid] < x)
l = mid + 1;
else if (a[mid] == x)
r = mid;
else
r = mid ;
}
return l;
}
int right_search(int x)
{//找c[i]中第一个比x大的数字
int l = 0, r = n;
while (l < r)
{
int mid = l + r >> 1;
if (c[mid] < x)
l = mid + 1;
else if (c[mid] == x)
l = mid + 1;
else
r = mid;
}
return l;
}
int main()
{
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);
long long res = 0;
for (int i = 0;i < n;i++)
{
int a1 = left_search(b[i]);
int c1 =n- right_search(b[i]);
res += 1LL * a1 * c1;
}
cout << res;
return 0;
}
桶+前缀和
我们发现本题A,B,C三个数组的数据范围很小,并且i,j,k不需要满足i<j<k
所以可以直接用桶记录下来,然后应用前缀和数组就可以求出<=x的所有数
即 sa[x]意义是:a数组中<=x的所有数的个数
sc[x]意义是:c数组中<=x的所有数的个数
这样子就可以算出所有的情况了
b[i]时,只要让 res+=sa[b[i]-1]*(n-sc[b[i]])
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e5+10;
int cnta[N];
int b[N];
int cntc[N];
int sa[N];
int sc[N];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
int a;cin>>a;
cnta[a]++;
}
for(int i=0;i<n;i++)
cin>>b[i];
for(int i=0;i<n;i++)
{
int c;cin>>c;
cntc[c]++;
}
sa[0]=cnta[0];sc[0]=cntc[0];
for(int i=1;i<N;i++)
sa[i]=cnta[i]+sa[i-1];
for(int i=1;i<N;i++)
sc[i]=cntc[i]+sc[i-1];
long long res=0;
for(int i=0;i<n;i++)
{
int aa=sa[b[i]-1];
int bb=n-sc[b[i]];
res+=1ll*aa*bb;
}
cout<<res;
}
如果题目要求了i<j<k
那么上面就只能用桶在前缀和的方法来做了,用这种方法,需要边枚举b[i],边装入桶中