题目:
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 ",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 ",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(); }