T24759 Cup#182-5 平行分班II

时间:2021-10-21 09:26:25

模拟赛不会系列。。。

这道题要求最小的极差(所选元素相差的最大值),最小的最大,想到了二分。

但是我菜,找不到什么单调性。

看了标程,发现可以用堆来搞定。相关算法可以从P1631和P2085看到。

思路是将每一组分别排序,然后每组预先选取第1到第c[i]个。算出一个最初的ans。

然后可以每次更新答案,删除掉其中的最小值,插入下一个元素到堆里面去。同时维护新的ans。

更新到其中任意一组没有下一个的时候就break掉。然后输出ans即可。

代码:

#include<cstdio>
#include<queue>
#include<algorithm>

const int maxn = 100005, maxm = 1000005;

struct Nodes
{
    int val, belong;
    bool operator < (const Nodes &rhs) const
    {
        return val > rhs.val;
    }
} s[maxm];
int tot;
std::priority_queue<Nodes> q;

int m[maxn], c[maxn];
int start[maxn], end[maxn], l[maxn], r[maxn];
int pos[maxn];
int maxx;
int n, ans;
bool cmp(const Nodes x, const Nodes y)
{
    return x.val < y.val;
}
int read()
{
    int ans = 0, s = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    return s * ans;
}
int main()
{
    n = read();
    for(int i = 1; i <= n; i++)
    {
        m[i] = read(), c[i] = read();
        start[i] = tot + 1, end[i] = tot + m[i];
        for(int j = 1; j <= m[i]; j++)
        {
            s[++tot] = (Nodes){read(), i};
        }
    }
    for(int i = 1; i <= n; i++) std::sort(s + start[i], s + end[i] + 1, cmp);
    for(int i = 1; i <= n; i++)
    {
        for(int j = start[i]; j <= start[i] + c[i] - 1; j++)
        {
            q.push(s[j]);
            maxx = std::max(maxx, s[j].val);
        }
        pos[i] = start[i] + c[i] - 1;
    }
    ans = maxx - q.top().val;
    while(2333)
    {
        Nodes u = q.top(); q.pop();
        int i = u.belong;
        if(pos[i] == end[i]) break;
        q.push(s[++pos[i]]);
        maxx = std::max(maxx, s[pos[i]].val);
        ans = std::min(ans, maxx - q.top().val);
    }
    printf("%d\n", ans);
    return 0;
}