BZOJ4653 [NOI2016]区间

Address


Solution

  • 考虑怎样快速动态地判断一组方案是否合法。
  • 显然可以用线段树维护,先对所有区间离散化,加入区间 [ l i , r i ] 时就把该区间内每个位置都加上 1,删去就是减去 1,并维护区间的最大值。
  • 记线段树根节点维护的最大值为 n u m ,则当 n u m = m 时,方案合法。
  • 进一步我们注意到,一组方案的花费只跟最长区间长度和最短区间长度有关。
  • 那么对于一组 m 个区间的合法方案,我们已知它的最长和最短区间长度,那么加入一些区间长度在最短到最长之间的区间,显然对花费没有影响。
  • 换句话说,如果我们把区间按照长度从大到小排序,任意取出其中一段并加入线段树中,若这时 n u m m ,则这段的所有区间可作为一种合法方案。
  • 并且这时因为已经排好序了,计算答案只要用最左边的区间长度减去最右边的区间长度。
  • 于是我们就可以用 尺取法 来计算最优的花费了。
  • 先把所有区间排好序,记录两个指针 i , j 表示合法方案选取的区间 [ j , i ]
    • 每次把指针 i 右移,将区间 [ l i , r i ] 加入线段树中。
    • 若此时 n u m m ,计算花费更新答案,之后删去区间 [ l j , r j ] ,并将指针 j 右移,重复这一步过程直到 n u m < m
  • i , j 的右移显然是 O ( n ) 的,加上线段树上每次 O ( log n ) 的操作,总复杂度 O ( n log n )

Code

#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>

using namespace std;

namespace inout
{
    const int S = 1 << 20;
    char frd[S], *ihed = frd + S;
    const char *ital = ihed;

    inline char inChar()
    {
        if (ihed == ital)
            fread(frd, 1, S, stdin), ihed = frd;
        return *ihed++;
    }

    inline int get()
    {
        char ch; int res = 0; bool flag = false;
        while (!isdigit(ch = inChar()) && ch != '-');
        (ch == '-' ? flag = true : res = ch ^ 48);
        while (isdigit(ch = inChar()))
            res = res * 10 + ch - 48;
        return flag ? -res : res;
    }

    char fwt[S], *ohed = fwt;
    const char *otal = ohed + S;

    inline void outChar(char ch)
    {
        if (ohed == otal)   
            fwrite(fwt, 1, S, stdout), ohed = fwt;
        *ohed++ = ch;
    }

    inline void put(int x)
    {
        if (x > 9) put(x / 10);
        outChar(x % 10 + 48);
    }   
};
using namespace inout;

const int Maxn = 0x3f3f3f3f;
const int N = 5e5 + 5, M = N << 3;
int tag[M], len[M], sum[M], b[N << 1];
int n, m, q, Ans = Maxn;

struct point
{
    int l, r, z;

    inline void scan()
    {
        l = get(); r = get(); z = r - l;
    }

    inline bool operator < (const point &x) const 
    {
        return z > x.z;
    }
}p[N];

#define sL s << 1
#define sR s << 1 | 1

inline int Max(int x, int y) {return x > y ? x : y;}

inline void Uptdate(int s)
{
    sum[s] = Max(sum[sL], sum[sR]); 
}

inline void addTag(int s, int v)
{
    tag[s] += v; sum[s] += v;
}

inline void pushDown(int s)
{
    if (tag[s])
    {
        addTag(sL, tag[s]);
        addTag(sR, tag[s]);
        tag[s] = 0;
    }
}

inline void Modify(int s, int l, int r, int x, int y, int v)
{
    if (l == x && r == y) return addTag(s, v);
    pushDown(s); int mid = l + r >> 1;
    if (y <= mid)
        Modify(sL, l, mid, x, y, v);
    else if (x > mid)
        Modify(sR, mid + 1, r, x, y, v);
    else 
    {
        Modify(sL, l, mid, x, mid, v);
        Modify(sR, mid + 1, r, mid + 1, y, v);
    }
    Uptdate(s);
}

inline void Build(int s, int l, int r)
{
    len[s] = r - l + 1;
    if (l == r) return ;
    int mid = l + r >> 1;
    Build(sL, l, mid); Build(sR, mid + 1, r);
}

inline void CkMin(int &x, int y) {if (x > y) x = y;}

int main()
{
    n = get(); q = get();
    for (int i = 1; i <= n; ++i)
        p[i].scan(), b[++m] = p[i].l, b[++m] = p[i].r;

    sort(b + 1, b + m + 1);
    m = unique(b + 1, b + m + 1) - b - 1;
    for (int i = 1; i <= n; ++i)
    {
        p[i].l = lower_bound(b + 1, b + m + 1, p[i].l) - b;
        p[i].r = lower_bound(b + 1, b + m + 1, p[i].r) - b;
    }
    sort(p + 1, p + n + 1);

    Build(1, 1, m);
    for (int i = 1, j = 1; i <= n; ++i)
    {
        Modify(1, 1, m, p[i].l, p[i].r, 1);
        while (j <= i && sum[1] >= q)
        {
            CkMin(Ans, p[j].z - p[i].z);
            Modify(1, 1, m, p[j].l, p[j].r, -1);
            ++j;
        }
    }

    printf("%d\n", Ans == Maxn ? -1 : Ans);
    return 0; 
}

猜你喜欢

转载自blog.csdn.net/bzjr_log_x/article/details/79430494