4345: [POI2016]Korale

50 篇文章 0 订阅
11 篇文章 0 订阅

4345: [POI2016]Korale

Time Limit: 30 Sec   Memory Limit: 256 MB
Submit: 316   Solved: 117
[ Submit][ Status][ Discuss]

Description

有n个带标号的珠子,第i个珠子的价值为a[i]。现在你可以选择若干个珠子组成项链(也可以一个都不选),项链的价值为所有珠子的价值和。现在给所有可能的项链排序,先按权值从小到大排序,对于权值相同的,根据所用珠子集合的标号的字典序从小到大排序。请输出第k小的项链的价值,以及所用的珠子集合。

Input

第一行包含两个正整数n,k(1<=n<=1000000,1<=k<=min(2^n,1000000))。
第二行包含n个正整数,依次表示每个珠子的价值a[i](1<=a[i]<=10^9)。

Output

第一行输出第k小的项链的价值。
第二行按标号从小到大依次输出该项链里每个珠子的标号。

Sample Input

4 10
3 7 4 3

Sample Output

10
1 3 4

HINT

Source

[ Submit][ Status][ Discuss]



先考虑第一问

将所有珠子按照价值升序排好,维护一个堆

堆中存放若干二元组(x,y),表示当前项链价值为x,最后一颗珠子是y

每次取出堆顶元素,计入答案,然后对于当前项链,选择新增下一颗珠子或是舍去最后一颗珠子并放入后一颗

这样重复k次就能得到第一问的答案了

第二问直接暴力搜索就行了

在用堆统计第一问答案时顺便记录权值和答案相同的项链的数量

每次贪心地选择编号最小的可以继续添加的珠子,即加入这颗珠子后项链的价值仍然不超过第一问答案

由于每新增一颗珠子就新增了一条项链,而有效项链条数是不超过k的

所以这样的搜索很快就能结束,查询下一颗珠子的工作可以交由线段树解决

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#define min(a,b) ((a) < (b) ? (a) : (b))
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int T = 4;
const int maxn = 1E6 + 10;
typedef long long LL;
 
struct data{
    LL sum; int pos; data(){}
    data(LL sum,int pos): sum(sum),pos(pos){}
    bool operator < (const data &B) const {return sum > B.sum;}
};
 
int n,m,res,stk[maxn],A[maxn],B[maxn],Min[maxn*T];
LL Ans;
 
priority_queue <data> Q;
 
inline void Build(int o,int l,int r)
{
    if (l == r) {Min[o] = A[l]; return;}
    int mid = l + r >> 1;
    Build(o<<1,l,mid); Build(o<<1|1,mid+1,r);
    Min[o] = min(Min[o<<1],Min[o<<1|1]);
}
 
inline int Query(int o,int l,int r,int pos,LL now)
{
    if (Min[o] > now) return maxn;
    if (l == r) return l; int mid = l + r >> 1;
    if (pos > mid) return Query(o<<1|1,mid+1,r,pos,now);
    int ret = Query(o<<1,l,mid,pos,now);
    return ret == maxn ? Query(o<<1|1,mid+1,r,pos,now) : ret;
}
 
inline void Dfs(LL sum,int tp,int pos)
{
    if (sum == Ans)
    {
        if (res == 1)
        {
            for (int i = 1; i <= tp; i++) printf("%d ",stk[i]);
            exit(0);
        }
        --res; return;
    }
    LL now = Ans - sum;
    for (int Nex = pos + 1; Nex <= n;)
    {
        int ret = Query(1,1,n,Nex,now);
        if (ret == maxn) return; stk[tp + 1] = ret;
        Dfs(sum + 1LL * A[ret],tp + 1,ret); Nex = ret + 1;
    }
}
 
inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    n = getint(); m = getint();
    for (int i = 1; i <= n; i++) A[i] = B[i] = getint();
    sort(B + 1,B + n + 1); Q.push(data(B[1],1));
    for (int i = 1; i < m; i++)
    {
        data k = Q.top(); Q.pop();
        if (k.sum == Ans) ++res; else Ans = k.sum,res = 1;
        if (k.pos == n) continue;
        Q.push(data(k.sum + 1LL * B[k.pos + 1],k.pos + 1));
        Q.push(data(k.sum + 1LL * (B[k.pos + 1] - B[k.pos]),k.pos + 1));
    }
    cout << Ans << endl;
    Build(1,1,n); Dfs(0,0,0);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值