题目链接:https://atcoder.jp/contests/abc155/tasks/abc155_d
题目大意
给定$N$个整数$A_1, A_2, \dots, A_N$, 求集合$S = \{A_i * A_j | 1 \leq i, j \leq N 且 i \neq j\}$的第$K$小。
分析
首先,通过把$N$个数分为正数,负数,零,再排下序,我们可以计算$S$中所有的正数,负数以及零的数目,进而可以判断出第$K$小的数是正数,是负数,还是零。
如果第$K$小的数是零,无需多言。
如果第$K$小的数不是零,则可以先求出上界与下界,通过二分法,指定所二分的数就是第$K$小的,然后用二分法计数有多少数比它小,直到上界等于下届为止。
时间复杂度为$O(Nlog^2N)$。
代码如下
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 /*-------------------Define Start-------------------*/ 5 typedef bool BL; // 布尔类型 6 typedef char SB; // 有符号1字节,8位 7 typedef unsigned char UB; // 无符号1字节,8位 8 typedef short SW; // 有符号短整型,16位 9 typedef unsigned short UW; // 无符号短整型,16位 10 typedef long SDW; // 有符号整型,32位 11 typedef unsigned long UDW; // 无符号整型,32位 12 typedef long long SLL; // 有符号长整型,64位 13 typedef unsigned long long ULL; // 无符号长整型,64位 14 typedef char CH; // 单个字符 15 typedef float R32; // 单精度浮点数 16 typedef double R64; // 双精度浮点数 17 18 #define Rep(i, n) for (register SDW i = 0; i < (n); ++i) 19 #define For(i, s, t) for (register SDW i = (s); i <= (t); ++i) 20 #define rFor(i, t, s) for (register SDW i = (t); i >= (s); --i) 21 #define foreach(i, c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i) 22 #define ms0(a) memset(a,0,sizeof(a)) 23 #define msI(a) memset(a,0x7f,sizeof(a)) 24 #define LOWBIT(x) ((x)&(-x)) 25 26 #define MP make_pair 27 #define PB push_back 28 #define ft first 29 #define sd second 30 #define ALL(x) x.begin(),x.end() 31 32 #define pr(x) cout << #x << " = " << x << " " 33 #define prln(x) cout << #x << " = " << x << endl 34 35 const ULL mod = 1e9 + 7; //常用模数(可根据题目需要修改) 36 const ULL inf = 0x7fffffff; //用来表示无限大 37 const ULL infLL = 0x7fffffffffffffffLL; //用来表示无限大 38 /*-------------------Define End-------------------*/ 39 40 const UDW maxN = 1e6 + 7; 41 SDW N, K; 42 vector< SLL > posi; // 存正数 43 SLL lenP; 44 vector< SLL > nega; // 存负数 45 SLL lenN; 46 SLL zero; // 记录0的个数 47 SLL cntP; // 正数数对个数 48 SLL cntN; // 负数数对个数 49 SLL cntZ; // 零数对个数 50 SLL ans; 51 52 void input(){ 53 SLL tmp; 54 55 cin >> N >> K; 56 Rep(i, N) { 57 cin >> tmp; 58 if(tmp == 0) { 59 ++zero; 60 } 61 else if(tmp > 0) { 62 posi.PB(tmp); 63 } 64 else { 65 nega.PB(tmp); 66 } 67 } 68 69 lenP = posi.size(); 70 lenN = nega.size(); 71 72 sort(ALL(posi)); 73 sort(ALL(nega)); 74 75 cntP = lenP * (lenP - 1) / 2 + lenN * (lenN - 1) / 2; 76 cntN = lenP * lenN; 77 cntZ = zero * (zero - 1) / 2 + zero * (lenP + lenN); 78 } 79 80 // 二分查找 A 中第一个满足 A * b > p的下标 81 SLL upper_bound(vector< SLL > &A, SLL b, SLL p) { 82 SLL L = 0; 83 SLL R = A.size(); 84 85 while(L != R) { 86 SLL mid = (L + R) >> 1; 87 88 if(A[mid] * b <= p) { 89 L = mid + 1; 90 } 91 else { 92 R = mid; 93 } 94 } 95 return R; 96 } 97 98 void solve(){ 99 if(K > cntN && K <= cntN + cntZ) { 100 ans = 0; 101 } 102 else if(K <= cntN) { // 第 K 小数对乘积为负数 103 SLL L = posi.back() * nega[0]; 104 SLL R = posi[0] * nega.back(); 105 106 while(L != R) { 107 SLL mid = (L + R) >> 1; 108 SLL sum = 0; // 记录小于等于mid的数对个数 109 110 Rep(i, lenP) { 111 SLL tmp = upper_bound(nega, posi[i], mid); 112 sum += tmp; 113 } 114 115 if(sum >= K) { 116 R = mid; 117 } 118 else { 119 L = mid + 1; 120 } 121 } 122 123 ans = R; 124 } 125 else { // 第 K 小数对乘积为正数 126 K -= cntN + cntZ; 127 reverse(ALL(nega)); // 由于负数 ×负数会改变数组的有序性,会使二分出错,所以这里反转一下 128 129 SLL L = infLL; 130 SLL R = 0; 131 132 if(lenP >= 2) { 133 L = min(L, posi[0] * posi[1]); 134 R = max(R, posi[lenP - 1] * posi[lenP - 2]); 135 } 136 137 if(lenN >= 2) { 138 L = min(L, nega[0] * nega[1]); 139 R = max(R, nega[lenN - 1] * nega[lenN - 2]); 140 } 141 142 while(L != R) { 143 SLL mid = (L + R) >> 1; 144 SLL sum = 0; // 记录小于等于mid的数对个数 145 146 Rep(i, lenP) { 147 SLL tmp = upper_bound(posi, posi[i], mid); 148 sum += tmp; 149 if(tmp > i) { 150 --sum; // 减去自己乘自己 151 } 152 } 153 154 Rep(i, lenN) { 155 SLL tmp = upper_bound(nega, nega[i], mid); 156 sum += tmp; 157 if(tmp > i) { 158 --sum; // 减去自己乘自己 159 } 160 } 161 162 sum /= 2; // 一个数对算了两遍,要除以2 163 164 if(sum >= K) { 165 R = mid; 166 } 167 else { 168 L = mid + 1; 169 } 170 } 171 172 ans = R; 173 } 174 } 175 176 void output(){ 177 cout << ans << endl; 178 } 179 180 int main() { 181 input(); 182 solve(); 183 output(); 184 return 0; 185 }
原文地址:https://www.cnblogs.com/zaq19970105/p/12339974.html
时间: 2024-10-08 11:07:03