【二分】四个数列

问题描述

ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。
请你帮帮他吧!

Input

第一行:n(代表数列中数字的个数,1≤n≤4000)
接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字(数字不超过 2 的 28 次方)

Output

输出不同组合的个数。

Sample

Input

6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46
26 -38 -10 62
-32 -54 -6 45

Output

5

思路

这是一道十分有趣的题目,直接爆搜n4的复杂度显然是过不去的,但是本题允许n2的复杂度,注意一共有四个数列,所以我们可以考虑拆成两部分,中间通过某种策略去搜索,显然我们可以通过有序二分的方式降低复杂度。先对前两个数列枚举求和排序,再枚举后两个数列的和,二分该和的相反数的左右区间,求差加一后累加,即可得到结果。

总结

二分时注意,要考虑边界的情况,根据二分策略对ans的初值进行调整,这样才可以保证二分出来的结果为满足该条件的第一个位置。

代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<vector>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define mem(a,s) memset(a,s,sizeof(a))
int a[4][4001];
int n;
vector<int> v;
int f1(int x){//>=的第一个位置,可能不是x ,若>=不存在,返回0 
 int l=0,r=n*n-1,ans=n*n;
 while(l<=r){
  int m=(l+r)>>1;
  if(v[m]>=x){
   ans=m;
   r=m-1;
  }else l=m+1;
 }
 return ans;
}
int f2(int x){//>x的第一个位置,若>不存在,返回0(边界点搜不到) 
 int l=0,r=n*n-1,ans=n*n;
 while(l<=r){
  int m=(l+r)>>1;
  if(v[m]>x){
      ans=m;
   r=m-1;
  }else l=m+1;
 }
 return ans;
}
int main(){
// freopen("1.txt","r",stdin);
 ios::sync_with_stdio(false);
 cin>>n;
 rep(i,1,n)
  cin>>a[0][i]>>a[1][i]>>a[2][i]>>a[3][i];
 rep(i,1,n)
  rep(j,1,n)
   v.push_back(a[2][i]+a[3][j]);
 sort(v.begin(),v.end());
 int sum=0;
 rep(i,1,n)
  rep(j,1,n){
  int x=-(a[0][i]+a[1][j]);
  int l=f1(x);
  int r=f2(x);
  //cout<<x<<' '<<l<<' '<<r<<' '<<l-r<<endl;
  sum+=(r-l);
 } 
 cout<<sum;
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值