zoukankan      html  css  js  c++  java
  • 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;
        }
            
    
        
        
    }
    
    
    
    
    
  • 相关阅读:
    判断两个对象是否相同
    参数的修饰符
    异常处理
    类型转换
    值类型和引用类型
    抽象方法实现计算器
    静态
    多态
    访问修饰符
    面向对象三大特性
  • 原文地址:https://www.cnblogs.com/BOZHAO/p/13707277.html
Copyright © 2011-2022 走看看