题目:https://www.luogu.org/problemnew/show/P1631
方法一:
数学知识:
1、不大于a[i]+b[j]的数至少有i*j个,所以
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
...
}
优化为
for(int i=1;i<=n;i++)
for(int j=1;i*j<=n;j++){
...
}
2、优化前时间复杂度为O(n^2),tle掉4个点,优化后时间复杂度为O(n/1+n/2+...+n/n)=O(nlogn),从而不会tle。
【说明】手工模拟很难写:参考样例:
3
126 168 240
117 193 276
方法二:
二分查找第n大的数。
设置指针i=1,j=n。i从左往右走,j从右往左走。
先固定i,然后让j--,直到a[i]+b[j]<=mid,累加变量tot+=j。
i++后,继续前一步,直到j==0。
然后比较tot与n的大小,如果tot<n,说明mid取得小了,则增大mid,否则减小mid。
其它同方法一。
AC代码(方法一):
#include<cstdio>
#include<cmath>
#include<cstring>#include<queue>
using namespace std;
priority_queue<int,vector<int>,greater<int> >q;
int n,a[100005],b[100005],k,x,y,s,t;
int main(){
cin>>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++)
for(int j=1;i*j<=n;j++)
//优化。不大于a[i]+b[j]的数共有i*j个
//O(n/1+n/2+...+n/n)=O(nlogn)
q.push( a[i]+b[j] );
printf("%d",q.top());
q.pop();
for(int i=2;i<=n;i++){
printf(" %d",q.top());
q.pop();
}
return 0;
}
AC代码(方法二):
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
priority_queue<int,vector<int>,greater<int> >q;
int n,a[100005],b[100005];
bool check(long long mid){
int i,j=n,tot=0;
for(i=1;i<=n;i++){
while(j>0 && a[i]+b[j]>mid)j--;
if(j==0)break;
tot+=j;
}
return tot<n;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
long long l=1,r=1e10,mid;
while(l<=r){
mid=(l+r)/2;
if( check(mid) )r=mid-1;
else l=mid+1;
}
for(int i=1;i<=n;i++)
for(int j=1;i*j<=n;j++)
if(a[i]+b[j]<=l) q.push( a[i]+b[j] );
else break;
printf("%d",q.top());
q.pop();
for(int i=2;i<=n;i++){
printf(" %d",q.top());
q.pop();
}
return 0;
}