• bzoj2811 [Apio2012]Guard


    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2811

    【题解】

    首先我们先把没看到忍者的段去掉,可以用线段树做。

    如果剩下的就是K,那么特判即可。

    我们可以把包含关系去掉然后对于剩下的区间,x单增,y单增。

    否则的话,我们有一个结论(挺显然的):只有每个区间的右段点才能成为答案。

    我们贪心地填肯定是填右端点。

    所以我们判断如果右端点填到了前一个位置是否可行即可,如果不可行那么必须填右端点。

    二分出这个位置所覆盖的区间(我们钦定填了这里)

    然后我们维护fp[i]表示左边i个区间最少填多少,fs[i]表示右边i个区间最少填多少(前缀、后缀)

    然后我们判断fp[l]+fs[r]+1是否大于K即可,如果大于,说明一定要填。

    # include <stdio.h>
    # include <string.h>
    # include <algorithm>
    // # include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    const int M = 5e5 + 10;
    const int mod = 1e9+7;
    
    # define RG register
    # define ST static
    
    int n, K, m;
    
    int x[M];
    struct guard {
        int l, r;
        guard() {}
        guard(int l, int r) : l(l), r(r) {}
        friend bool operator < (guard a, guard b) {
            return a.l < b.l || (a.l == b.l && a.r > b.r);
        }
    }a[M], o[M]; int on;
    
    bool del[M];
    int new_id[M], idx, old_id[M];
    int pre[M], nxt[M];
    int fp[M], fs[M];
    
    namespace SMT {
        int w[M];
        # define ls (x<<1)
        # define rs (x<<1|1)
        inline void edt(int x, int l, int r, int L, int R) {
             if(w[x]) return;
            if(L <= l && r <= R) {
                w[x] = 1;
                return ;
            }
            int mid = l+r>>1;
            if(L <= mid) edt(ls, l, mid, L, R);
            if(R > mid) edt(rs, mid+1, r, L, R);
        }
        inline int query(int x, int l, int r, int pos) {
            if(w[x]) return 1;
            if(l == r) return 0;
            int mid = l+r>>1;
            if(pos <= mid) return query(ls, l, mid, pos);
            else return query(rs, mid+1, r, pos);
        }
        # undef ls
        # undef rs
    }
    
    
    int main() {
        scanf("%d%d%d", &n, &K, &m);
        for (int i=1; i<=m; ++i) scanf("%d%d%d", &a[i].l, &a[i].r, x+i);
        for (int i=1; i<=m; ++i) if(x[i] == 0) SMT::edt(1, 1, n, a[i].l, a[i].r);
        for (int i=1; i<=n; ++i) {
            if(SMT::query(1, 1, n, i) == 0) {
                new_id[i] = ++idx, old_id[idx] = i;
    //            printf("i = %d, idx = %d
    ", i, idx);
            }
            else del[i] = 1;
        }
        if(idx == K) {
            for (int i=1; i<=n; ++i) 
                if(!del[i]) printf("%d
    ", i);
            return 0;
        }
        for (int i=1; i<=n; ++i) {
            if(!del[i]) pre[i] = i;
            else pre[i] = pre[i-1];
        }
        for (int i=n; i; --i) {
            if(!del[i]) nxt[i] = i;
            else nxt[i] = nxt[i+1];
        }
        for (int i=1; i<=m; ++i) {
            int nl = nxt[a[i].l], nr = pre[a[i].r];
            if(nl <= nr) o[++on] = guard(new_id[nl], new_id[nr]);
        }
        sort(o+1, o+on+1);
        m = 0;
        for (int i=1; i<=on; ++i) {
            while(m && o[i].l >= a[m].l && o[i].r <= a[m].r) --m;
            a[++m] = o[i];
        }
    //    for (int i=1; i<=m; ++i) 
    //        printf("%d %d
    ", a[i].l, a[i].r);
        int cur = 0;
        for (int i=1; i<=m; ++i) 
            if(a[i].l > cur) fp[i] = fp[i-1] + 1, cur = a[i].r;
            else fp[i] = fp[i-1];
        cur = 1e9;
        for (int i=m; i; --i)
            if(a[i].r < cur) fs[i] = fs[i+1] + 1, cur = a[i].l;
            else fs[i] = fs[i+1];
        
        bool have_ans = 0; 
        
        for (int i=1, x, l, r, ans1, ans2; i<=m; ++i) {
            if(fp[i] != fp[i-1]+1) continue;
            if(a[i].l == a[i].r) {
                have_ans = 1;
    //            printf("=%d
    ", a[i].r);
                printf("%d
    ", old_id[a[i].r]);
                continue;
            }
            // 考察每个区间的右端点是否可行
            x = a[i].r-1;
            l = 1, r = i-1, ans1 = 0;
            while(1) {
                if(r-l<=3) {
                    for (int j=r; j>=l; --j) 
                        if(a[j].r < x) {
                            ans1 = j;
                            break;
                        }
                    break;
                }
                int mid = l+r>>1;
                if(a[mid].r < x) l = mid;
                else r = mid;
            }
            l = i+1, r = m, ans2 = m+1;
            while(1) {
                if(r-l<=3) {
                    for (int j=l; j<=r; ++j)
                        if(a[j].l > x) {
                            ans2 = j;
                            break;
                        }
                    break;
                }
                int mid = l+r>>1;
                if(a[mid].l > x) r = mid;
                else l = mid;
            }
    //        printf("%d %d
    ", ans1, ans2);
            if(fp[ans1] + fs[ans2] + 1 > K) {
    //            printf("=%d
    ", a[i].r);
                printf("%d
    ", old_id[a[i].r]);
                have_ans = 1;
            } 
        }
        
        if(!have_ans) puts("-1");
    
        return 0;
    }
    View Code
  • 相关阅读:
    BigDecimal加减乘除计算方式
    Element-UI 关于table中fixed使用和table样式混乱问题处理
    java集合框架中contains(),containsKey()和containsValue()的用法
    Vue中子组件watch监听props中父组件对象的变化的坑
    js中使用splice在一次循环删除数组中的多个元素
    Java中instanceof关键字的理解
    List.contains(Object object)方法,比较对象是否相同
    Vue 动态路由的实现以及 Springsecurity 按钮级别的权限控制
    sql中#与$的区别
    Vue自定义指令实现按钮级权限控制功能
  • 原文地址:https://www.cnblogs.com/galaxies/p/bzoj2811.html
Copyright © 2020-2023  润新知