• bzoj2811[Apio2012]Guard 贪心


    2811: [Apio2012]Guard

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 905  Solved: 387
    [Submit][Status][Discuss]

    Description

    Input

    Output

    Sample Input

    5 3 4
    1 2 1
    3 4 1
    4 4 0
    4 5 1

    Sample Output

    3
    5

    HINT

    在这个样例中,有两种可能的安排方式:1,3,5 或者 2,3,5。即 3 和 5
    后面必然躲着一个忍者。
    考虑第一个灌木丛,存在一种安排方案使得它的后面躲着忍者,但也存在一
    种安排方案使得它后面没有躲忍者,因此不应该输出 1。同理,不应该输出 2。

     

    贪心。
    把所有不可能出现忍者的位置除去,重新编号序列  可以用差分实现
    对区间进行操作,对于可能存在忍者的区间,把它缩小到没有不可能出现忍者的位置,并去除包含关系
    这样,剩下的区间就只能是l递增,r递增的了
    对于每个剩下的区间,我们都要选一个区间内的点,根据贪心来说,选右端点最优
    因为在几个区间重复的部分选一点可以使这几个区间都被处理,右端点最可能被几个区间包含

    因此我们得到贪心策略:
    对于每一个有效区间,选择右端点可以使满足每个区间的要求(当然有些区间包含之前选择的右端点就不选了),并让已知位置的忍者数最少


    但是每个区间我们不一定要选择r点,还可能选择r-1,r-2,r-3...这些点来使得区间满足
    现在我们要判断的是对于某一个区间,我们是否能唯一选择r点必定出现忍者

    我们假设在r-1放一个忍者来满足当前区间且在r不放忍者,如果确实可以满足那么r点不一定会出现忍者
    预处理出f[i],g[i]表示从前向后,从后向前选择点使得 到i的前缀,后缀区间都满足
    设r-1不能满足的向前数第一个区间为t1,向后数第一个区间为t2   那么r-1不可行的条件就是f[t1]+g[t2]+1>K

    至于为什么要选择r-1来判断r可行, 是因为r-1更有可能被更多的区间包含,让r不一定出现忍者的期望更大

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #define ll long long
    #define N 100050
    using namespace std;
    int n,m,k,cnt,val[N],f[N],g[N],cf[N],ql[N],qr[N];
    struct query{int l,r,c;}q[N];
    vector<pair<int,int> >p;
    int main(){
        scanf("%d%d%d",&n,&k,&m);
        
        //重新构造序列 
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].c);
            if(!q[i].c)cf[q[i].l]++,cf[q[i].r+1]--;
        }
        int now=0;
        for(int i=1;i<=n;i++){
            now+=cf[i];
            if(now<=0)ql[i]=qr[i]=++cnt,val[cnt]=i;
        }
        if(cnt==k){
            for(int i=1;i<=cnt;i++)printf("%d
    ",val[i]);
            return 0;
        }
        for(int i=1;i<=n;i++)if(!qr[i])qr[i]=qr[i-1];
        ql[n+1]=n+1;
        for(int i=n;i;i--)if(!ql[i])ql[i]=ql[i+1];
        for(int i=1;i<=m;i++){
            if(!q[i].c)continue;
            int l=q[i].l,r=q[i].r;
            l=ql[l];r=qr[r];
            if(l>r)continue;
            p.push_back(make_pair(l,r));
        }
        sort(p.begin(),p.end());
        int tp=0,fg=0;
        for(int i=0;i<p.size();i++){
            while(tp&&p[i].first>=ql[tp]&&p[i].second<=qr[tp])tp--;
            ql[++tp]=p[i].first;qr[tp]=p[i].second;
        }
        //重新构造序列 
        
        int R=0,L=n+1;
        for(int i=1;i<=tp;i++){
            if(ql[i]>R)f[i]=f[i-1]+1,R=qr[i];
            else f[i]=f[i-1];
        }
        for(int i=tp;i;i--){
            if(qr[i]<L)g[i]=g[i+1]+1,L=ql[i];
            else g[i]=g[i+1];
        }
        for(int i=1;i<=tp;i++){
            if(f[i]==f[i-1])continue;
            if(ql[i]==qr[i]){
                printf("%d
    ",val[ql[i]]);
                fg=1;continue;
            }
            int x=qr[i]-1,l=1,r=i-1,t1=0,t2=tp+1;
            while(l<=r){
                int mid=l+r>>1;
                if(qr[mid]<x)t1=mid,l=mid+1;
                else r=mid-1;
            }
            l=i+1;r=tp;
            while(l<=r){
                int mid=l+r>>1;
                if(ql[mid]>x)t2=mid,r=mid-1;
                else l=mid+1;
            }
            if(f[t1]+g[t2]+1>k){
                printf("%d
    ",val[qr[i]]);
                fg=1;
            }
        }
        if(!fg)puts("-1");
        return 0;
    }

  • 相关阅读:
    基础数据补充
    购物车
    小数据池、深浅拷贝和集合
    列表、元组和range
    小数据池、深浅拷贝和集合练习
    字典
    字符串练习
    列表练习
    练习
    字典练习
  • 原文地址:https://www.cnblogs.com/wsy01/p/8074534.html
Copyright © 2020-2023  润新知