首页 > 其他分享 >Smallest Subsets

Smallest Subsets

时间:2022-09-21 09:24:29浏览次数:88  
标签:Subsets int sum pos rd Smallest include minus

传送门

题意:\(n\le 1e5\) 个整数,范围 \(-1e9\le a\le 1e9\),求第 \(k\) 小的子集和


思路

先考虑只有正数怎么做

这就是经典的类最短路:

先将序列排序,然后从最小的子集和 \({a_1}\) 出发

每次加入 \(S-a_{Mx}+a_{Mx+1}\) 和 \(S+a_{Mx+1}\),其中 \(Mx\) 表示 \(S\) 中最大数的下标

维护一个优先队列,每次弹出 \(k\) 以后的元素即可

再来考虑有负数的情况

假设序列中的所有负数和为 \(minus\)

那显然我们是从 \(minus\) 出发,每次加上一个正数,或者去掉一个负数

那我们可以考虑将所有负数先取反变成正数,按照上面的方法跑一遍,最后每个求出来的子集和都加上 \(minus\) 即可


代码

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
#define PFOR(i, x) for(int i = he[x]; i; i = r[i].nxt)
inline int rd()
{
    int sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
int n, m, a[100005];
LL minus, ans;
#define pli std::pair<LL, int>
std::multiset<pli> q;
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = rd(), m = rd() - 1;
    FOR(i, 1, n)
    {
        a[i] = rd();
        if(a[i] < 0) minus += a[i], a[i] = -a[i];
    }
    printf("%lld\n", minus);
    std::sort(a + 1, a + 1 + n);
    q.insert(pli(a[1], 1));
    while(m)
    {
        ans = (*q.begin()).first;
        printf("%lld\n", ans + minus);
        int pos = (*q.begin()).second;
        if(pos < n)
        {
            LL sum = ans - a[pos] + a[pos + 1];
            q.insert(pli(sum, pos + 1));
            sum = ans + a[pos + 1];
            q.insert(pli(sum, pos + 1));
        }
        m--; q.erase(q.begin());
    }
    return 0;
}

标签:Subsets,int,sum,pos,rd,Smallest,include,minus
From: https://www.cnblogs.com/zuytong/p/16714410.html

相关文章