N<=1e5,基本上可以得出这道题算法复杂度如果在 O(n^2) 及以上都是过不了的,不过秉承着蓝桥杯是暴力杯的原则,可以从朴素算法一步一步开始做。
TLE代码 O(n^3) 朴素算法 60pts
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+1;
int n,a[N],b[N],c[N];
long long ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL),cout.tie(NULL);
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];
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
for(int k=1;k<=n;++k)
ans+=(a[i]<b[j] and b[j]<c[k]);
cout<<ans;
return 0;
}
跟着题目模拟一遍,就拿到了7个样例点。有这还要什么自行车
这道题和a+b+c=d这种数对都存在共性,模拟的时候会发现第一列的数值被重复跑了n次
即计算4的时候,1->3->4 , 2->3->4,对于5来说也是,1->3->5 , 2->3->5
前面两段被重复计算了,所以可以先算出中间那一列可以被连上的情况,减少重复计算。
TLE代码 O(n^2) 优化暴力 84pts
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+1;
int n,a[N],b[N],c[N],cnt[N];
long long ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL),cout.tie(NULL);
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];
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
cnt[i]+=b[i]>a[j];
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(c[j]>b[i])ans+=cnt[i];
cout<<ans;
return 0;
}
到这一步之后可以发现,暴力已经没什么地方可以优化了,只能从核心入手对公式进行整改。
显然b序列5可以被a连上的所有情况是
即累计在a序列中存在比b[1]小的数,那就直接累计比b[1]小的数出现的次数即可,将式子变为
遍历a序列用桶记录一下所有数出现的次数(哈希)即可解决问题,其中求和式子可以应用前缀和做到O(1)查询。
AC代码 O(n) 100pts
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+1;
long long n,a[N],b[N],c[N],cnt[N],tmp,ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL),cout.tie(NULL);
cin>>n;
for(int i=1;i<=n;++i)cin>>tmp,a[tmp]++;
for(int i=1;i<=n;++i)cin>>tmp,b[tmp]++;
for(int i=1;i<=n;++i)cin>>tmp,c[tmp]++;
for(int i=1;i<N;++i)a[i]+=a[i-1];
for(int i=1;i<N;++i)cnt[i]=b[i]*a[i-1];;
for(int i=1;i<N;++i)cnt[i]+=cnt[i-1];
for(int i=1;i<N;++i)ans+=c[i]*cnt[i-1];
cout<<ans;
return 0;
}