zoukankan      html  css  js  c++  java
  • AtCoder ABC 155D Pairs

    题目链接: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 eq 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 }
    View Code
  • 相关阅读:
    C# List的深复制(转)
    asp.net core控制台项目运行
    进度条界面控件
    给窗体做遮罩(另类做法)
    显示列表控件(引用SourceGrid)
    TimeExit 界面无点击定时退出类
    TimeHelp 获取时间戳转换类
    复旦大学2016--2017学年第一学期高等代数I期末考试情况分析
    复旦大学高等代数历届每周一题汇总
    复旦高等代数 I(16级)每周一题
  • 原文地址:https://www.cnblogs.com/zaq19970105/p/12339974.html
Copyright © 2011-2022 走看看