第k小子集和[双指针+meet in the middle]

给出一个长度 \(n\leq 35\) 的序列,求他的子集和的第 \(k\)

把前 n/2 的子集和放到一个数组里 后 n/2 的放到另一个数组里,然后问题转化为了求各取一个数和的第 \(k\)

可以二分第 \(k\) 小的数是多少

namespace QvvQ {
vll t1, t2;
int n, a[50];
ll k;
void dfs(int pos, int r, ll sum, vll &s) {
  if (pos > r) return void(s.pb(sum));
  dfs(pos + 1, r, sum + a[pos], s), dfs(pos + 1, r, sum, s);
}
void init() {

} 

bool check(ll X) {//\leq X 的数对的数量是否<k
  ll cnt = 0;
  for (int p1 = 0, p2 = t2.size() - 1; p1 != t1.size(); ++p1) {
    while (p2 > 0 && t1[p1] + t2[p2] > X) --p2;
    if (t1[p1] + t2[p2] <= X) cnt += p2 + 1;
    if (cnt >= k) return 0;
  }
  return 1;
}

void solve() {
  n = in, k = in;
  lo1(i, n) in, a[i];
  dfs(1, n >> 1, 0, t1), dfs(n / 2 + 1, n, 0, t2);
  sort(all(t1)), sort(all(t2));
  ll l = 1, r = t1.back() + t2.back();
  while (l <= r) {
    if (check(mid)) l = mid + 1;
    else r = mid - 1;
  }
  out, l;//l-1+1 l-1是最后一个合法(<k)的
} 
 
}

int main() {
#ifdef QvvQ
  // freopen("data.in", "r", stdin);
  // freopen("data.out", "w", stdout);
  Dbg = 1;
#endif
  int T = 1;
  while (T--) QvvQ::init(), QvvQ::solve();
#ifdef QvvQ
  fprintf(stderr, "\ntime:%.5fms", clock() * 1000.0 / CLOCKS_PER_SEC);
#endif
  return 0;
}
posted @ 2019-03-03 16:35  QvvQ  阅读(369)  评论(0编辑  收藏  举报