洛谷 P1631 序列合并 二分答案

题目描述

有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到 N^2N2 个和,求这 N^2N2 个和中最小的N个。

输入输出格式

输入格式:

 

第一行一个正整数N;

第二行N个整数 A_iAi , 满足 A_i\le A_{i+1}AiAi+1 且 A_i\le 10^9Ai109 ;

第三行N个整数 B_iBi , 满足 B_i\le B_{i+1}BiBi+1 且 B_i\le 10^9Bi109 .

【数据规模】

对于50%的数据中,满足1<=N<=1000;

对于100%的数据中,满足1<=N<=100000。

 

输出格式:

 

输出仅一行,包含N个整数,从小到大输出这N个最小的和,相邻数字之间用空格隔开。

 

输入输出样例

输入样例#1: 复制
3
2 6 6
1 4 8
输出样例#1: 复制
3 6 7

一开始写的是单调队列,但是写着写着就懵掉了,情况没考虑全,本来想看看题解里有没有同样做法的补充下思路,结果看到了一个牛逼(暴力)的思路--二分
首先,对于这n^2个数的和,肯定是不能暴力求和+排序的,10^5范围时间必爆无疑,但很显然后面的大数字根本用不到,所以我们考虑不计算这部分的值,这
就要求我们要知道最大的最小值是多少(后面的就不用算了),求最大的最小值,很容易联想到二分,二分出最大的最小值后挨个计算范围内的值,排序后输出即可
完整代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
int n,ans[MAXN],a[MAXN],b[MAXN],cnt;
int read()
{
    char ch=getchar();long long ans=0,k=1; 
    while(ch<'0'||ch>'9'){if(ch=='-') k=-1; ch=getchar();} 
    while(ch<='9'&&ch>='0'){ans=ans*10+ch-'0'; ch=getchar();}
    return ans*k;
}
bool judge(int x)
{
    int sum=0;//能选几个数
    for(int i=1;i<=n;i++)
       {
        if(sum>n||a[i]+b[1]>x)break;//如果选的数超过了n或任意a[i]与b[1]的和大于当前选择的最小值,退出循环
//因为序列是单调增的,只要一个不合法,后面的肯定也不合法
for(int j=1;j<=n;j++) { if(a[i]+b[j]<x)//如果能用 { sum++;//计数器++ if(sum>n)break;//同上 } if(a[i]+b[j]>x)break;//同上 } } if(sum>n)return 0;//同上 return 1; } int main() { n=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++) b[i]=read(); int l=a[1]+b[1],r=a[n]+b[n];//二分最大的最小值,边界是最小的和与最大的和 while(l!=r)//二分版子 { int mid=(l+r)/2; if(judge(mid)) l=mid+1; else r=mid; }
//二分后l即时最大的最小值,也就是下面的边界
for(int i=1;i<=n;i++) { if(a[i]+b[1]>l)break;//同上分析 for(int j=1;j<=n;j++) if(a[i]+b[j]<=l) ans[++cnt]=a[i]+b[j];//保存 else break; } sort(ans+1,ans+cnt+1);//排序 for(int i=1;i<=n;i++) cout<<ans[i]<<' '; //输出前n个 return 0; }

参考大佬@ laorui的题解

posted @ 2018-07-23 21:51  pcpcppc  阅读(178)  评论(0编辑  收藏  举报