洛谷 P8667 [蓝桥杯 2018 省 B] 递增三元组


 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;
}

猜你喜欢

转载自blog.csdn.net/qq_17807067/article/details/130001363