zoukankan      html  css  js  c++  java
  • [差分][二分][贪心]luogu P3634 [APIO2012]守卫

    题面

    https://www.luogu.com.cn/problem/P3634

    给m个限制,可以是一段区间中必须有或者必须无忍者

    最多有k个忍者,问有多少个位点一定有忍者

    分析

    首先用差分标记一下0忍者的区间,去掉

    然后再删去包含了其他区间的区间,没有意义

    将剩余区间按左端点排序,方便处理

    考虑计算处理到第i个区间所需的最少忍者数,如果该区间内没有已经被选择的点,贪心选择右端点使总点数最少

    倒着处理一遍,类似,不过是贪心选择左端点

    枚举每个右端点考虑不选择它而选择它的左侧端点,并二分找到不与它相交的最近的左右两个区间

    若从左侧开始处理到左侧区间的最少忍者数+从右侧开始处理到右侧区间的最少忍者数+1(选择当前区间)的所需忍者数大于k,那么说明不选这个右端点是不合法的,所以这个点必选

    代码

    #include <iostream> 
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N=1e5+10;
    struct Guard {
        int a,b,c;
    }g[N];
    int n,k,m,zm,gcnt;
    int zer[N],ref[N],rcnt,nearl[N],nearr[N],f[N],h[N];
    bool ans;
    
    bool CMP(Guard a,Guard b) {return a.a<b.a;}
    
    int main() {
        scanf("%d%d%d",&n,&k,&m);
        for (int i=1;i<=m;i++) {
            scanf("%d%d%d",&g[i].a,&g[i].b,&g[i].c);
            if (!g[i].c) zer[g[i].a]++,zer[g[i].b+1]--;
        }
        for (int i=1;i<=n;i++) {
            zer[i]+=zer[i-1];
            if (!zer[i]) ref[nearl[i]=nearr[i]=++rcnt]=i;
            else nearl[i]=n+1;
        }
        if (rcnt==k) {for (int i=1;i<=rcnt;i++) printf("%d
    ",ref[i]);return 0;}
        for (int i=2;i<=n;i++) nearr[i]=max(nearr[i],nearr[i-1]),nearl[n-i+1]=min(nearl[n-i+1],nearl[n-i+2]);
        for (int i=1;i<=m;i++) if (g[i].c&&nearl[g[i].a]<=nearr[g[i].b]) g[++zm].a=nearl[g[i].a],g[zm].b=nearr[g[i].b];
        sort(g+1,g+zm+1,CMP);
        for (int i=1;i<=zm;i++) {
            while (gcnt&&g[gcnt].a<=g[i].a&&g[i].b<=g[gcnt].b) gcnt--;
            g[++gcnt]=g[i];
        }
        for (int i=1,r=0;i<=gcnt;i++) if (g[i].a>r) f[i]=f[i-1]+1,r=g[i].b; else  f[i]=f[i-1];
        for (int i=gcnt,l=n+1;i;i--) if (g[i].b<l) h[i]=h[i+1]+1,l=g[i].a; else h[i]=h[i+1];
        for (int i=1,l,r,mid,ansl,ansr;i<=gcnt;i++)
            if (f[i]!=f[i-1])
                if (g[i].a==g[i].b) printf("%d
    ",ref[g[i].b]),ans=1;
                else {
                    l=1;r=i-1;ansl=0;
                    while (l<=r) {
                        mid=l+r>>1;
                        if (g[mid].b<g[i].b-1) l=mid+1,ansl=mid; else r=mid-1;
                    }
                    l=i+1;r=gcnt;ansr=n+1;
                    while (l<=r) {
                        mid=l+r>>1;
                        if (g[mid].a>g[i].b-1) r=mid-1,ansr=mid; else l=mid+1;
                    }
                    if (f[ansl]+1+h[ansr]>k) printf("%d
    ",ref[g[i].b]),ans=1;
                }
        if (!ans) printf("-1
    ");
    }
    View Code
    在日渐沉没的世界里,我发现了你。
  • 相关阅读:
    7-3.自定义列表
    GoLang 使用协程与管道随机生成姓名
    [Unity3D] 点击物品显示物品信息
    [Unity3D] 碰撞物体添加到背包
    [PS] DDS文件导入插件
    [Unity3D] 给角色添加武器
    [Unity3D] 刚体 碰撞器 触发器
    [Unity3D] 物体的几种移动方法
    [Unity3D] 解决导入的模型出现闪烁的问题
    [Unity3D] 人物角色跳跃(动画跳跃&刚体跳跃)
  • 原文地址:https://www.cnblogs.com/mastervan/p/14553203.html
Copyright © 2011-2022 走看看