P1712 [NOI2016]区间

P1712 [NOI2016]区间

题意:

给你n个线段让你找出\(m\)个线段且这个\(m\)个线段都有一个公共点\(x\),对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。问合法选取的方案花费最小是?

题解:

由于数据太大,首先肯定能想到离散化。

想到离散化就会想到线段树去维护区间信息,对与区间(l, r)就在线段是将区间(l, r)的信息加1就行了。

但是问题来了。

如何才能找到m个线段都有公共点且花费最小呢?

如果让所有线段都从小答案排序, 那么答案是不是一定在这个排序后里面连续的一段?

如果不明白就仔细想一想。

如果知道了这个。那不就简单了。

用线段树维护,每个简单被覆盖的次数, 如果 \(tree[1] >= m\)说明有一个点的覆盖次数大于m,也就说明有m个线段相交一点, 那怎么找到m个线段花费呢?

我们用个\(last\) 初始为1, 我首先开始向线段树里面插入线段。

\(tree[1] >= m\)

就移动\(last\) 直到 \(tree[1] < m\) 那么答案就是 当前线段的长度减去 \(last - 1\)的那个线段的长度

然后继续插入线段 重复上述操作。

为啥这样就一定能得到答案, 因为线段是按长度从小到达排序, 自己想一想就明白了。有点向滑动窗口

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7;

int n, sum;


int tree[10 * N];

vector<int> g;

#define m (l + r) / 2
#define lson 2 * node
#define rson 2 * node + 1

int add[4 * N];



struct line {
    int x, y;
} lin[N];

bool cmp(line x, line y) {
    return (x.y - x.x)  < (y.y - y.x); 
}

int get_id(int x) {
    return lower_bound(g.begin(), g.end(), x) - g.begin() + 1;
}

void push_down(int node) {
    if (add[node]) {
        tree[lson] += add[node];
        tree[rson] += add[node];
        add[lson] += add[node];
        add[rson] += add[node];
        add[node] = 0;
    }

}

void update(int v, int ql, int qr, int l, int r, int node) {
    if (ql <= l && qr >= r) {
        tree[node] += v;
        add[node] += v;
     
        return;
    }
    push_down(node);
    if (ql <= m) update(v, ql, qr, l, m, lson);
    if (qr > m) update(v, ql, qr, m + 1, r, rson);
    tree[node] = max(tree[lson], tree[rson]);
}





int main() {
  
    scanf("%d %d", &n, &sum);
    for (int i = 1; i <= n; i++) {
        int x, y;
        scanf("%d %d", &x, &y);
        g.push_back(x);
        g.push_back(y);
        lin[i].x = x, lin[i].y = y;
    } 
    sort(g.begin(), g.end());
    g.erase(unique(g.begin(), g.end()), g.end());
    sort(lin + 1, lin + n + 1, cmp);

    int last = 1;
    int ans = INT_MAX;
    for (int i = 1; i <= n; i++) {
        int l = get_id(lin[i].x);
        int r = get_id(lin[i].y);

        update(1, l, r, 1, g.size(), 1);

        if (tree[1] == sum) {

            while (tree[1]>= sum) {
                l = get_id(lin[last].x);
                r = get_id(lin[last].y);
                update(-1, l, r, 1, g.size(), 1);
       
                last++;
            }
            ans = min(ans, (lin[i].y - lin[i].x) - (lin[last - 1].y - lin[last - 1].x));
        }  

    }
    if (ans == INT_MAX) {
        cout << -1 << endl;
    } else {
        cout << ans << endl;
    }
        

    
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值