北京集训TEST13——PA(第k小数)

题目:

Description

【问题描述】

    从n个数中选若干(至少1)个数求和,求所有方案中第k小的和(和相同但取法不同的视为不同方案)。
【输入格式】
    第一行输入2个正整数n,k。
    第二行输入这n个正整数。
【输出格式】
    输出第k小的和。
【样例输入】
5 12
1 2 3 5 8
【样例输出】
8
【样例解释】
    前12小的和分别为:1 2 3 3 4 5 5 6 6 7 8 8
【数据规模和约定】
    测试点1,n<=16。
    测试点2-3,n<=100,k<=500。
    测试点4-5,n<=1000,k<=5000。
    测试点6-8,n<=10^5,k<=5*10^5。
    测试点9-10,n<=35。
    对于所有数据,1<=k<2^n,n个正整数每个都不超过10^9。


题解:

  80%数据:用二元组(sum,i)表示和为sum,当前考虑第i个位置是否要选,以sum+a[i]为权值维护。
每次扩展到(sum+a[i],i)和(sum,i+1)。
时间复杂度O(KlogK)
剩下20%数据:二分答案以后折半搜索,时间复杂度O(2^{K/2}logW)

心得:

  优先队列是个好东西,折半搜索是个好东西;

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cctype>
#include<queue>
using namespace std;
const int N=100005;
const int M=1e7+5;
struct node
{
  long long sum;
  int pos;
  node(long long sum,int pos):sum(sum),pos(pos){}
};
inline bool operator < (const node &a,const node &b)
{
  return a.sum>b.sum;
}
priority_queue<node>que;
long long num[N];
long long vala[M],valb[M];
long long k;
int tota=0,totb=0;
int n;
void solve1()
{
  que.push(node(num[1],2));
  for(int i=1;i<k;i++)
  {
    long long tsum=que.top().sum;
    int tpos=que.top().pos;
    que.pop();  
    if(tpos<=n)
    { 
      que.push(node(tsum-num[tpos-1]+num[tpos],tpos+1));
      que.push(node(tsum+num[tpos],tpos+1));
    }
  }
  printf("%lld\n",que.top().sum);
}
void dfs1(int u,int des,long long val)
{
  if(u>des)
  {  
    if(val) 
      vala[++tota]=val;
  }
  else
  {  
    dfs1(u+1,des,val+num[u]);
    dfs1(u+1,des,val);
  }
}
void dfs2(int u,int des,long long val)
{
  if(u>des)
    if(val) 
      valb[++totb]=val;
  else
  {  
    dfs2(u+1,des,val+num[u]);
    dfs2(u+1,des,val);
  }
}
bool jud(long long val)
{
  long long temp=0;
  int j=1;
  for(int i=tota;i>=0;i--)
  {
    if(vala[i]>val)  continue;
    for(;j<=totb&&vala[i]+valb[j]<=val;j++);
    temp+=j;
  }
  return temp>k;
}
void solve2()
{
  dfs1(1,n/2,0);
  dfs2(n/2+1,n,0);
  sort(vala+1,vala+tota+1);
  sort(valb+1,valb+totb+1);
  long long l=0,r=0x3f3f3f3f3f3f3f3f;
  while(l<=r)
  {
    long long mid=(l+r)/2;
    if(jud(mid))  r=mid-1;
    else l=mid+1;
  }
  printf("%lld\n",r+1);
}
int main()
{
  scanf("%d%lld",&n,&k);
  for(int i=1;i<=n;i++)
    scanf("%lld",&num[i]);
  sort(num+1,num+n+1);
  long long j=0x3f3f3f3f3f3f3f3f;
  if(n>35)  solve1();
  else solve2();
}  

 

posted @ 2017-03-23 07:49  AseanA  阅读(181)  评论(0编辑  收藏  举报