p3634 [APIO2012]守卫

Posted 水题收割者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了p3634 [APIO2012]守卫相关的知识,希望对你有一定的参考价值。

传送门

分析

1.先预处理出不被0覆盖的点,然后对每个点处理出在它左边离他最近的点和在他右边理他最近的点。

2.对于每个至少存在一个忍者的区间,先将它左右边界处理为不被0所覆盖。排序后将包含其他区间的区间去除。

3.贪心求出前i个区间最小忍者数和后i个区间最小忍者数。

4.我们知道对于一个区间除了点R[i]之外最优的点就是R[i]-1,所以我们二分一个右端点小于R[i]-1的最靠右区间k1和一个左端点大于R[i]-1的最靠左区间k2,如果f[k1]+g[k2]+1>k则代表点R[i]必选

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
struct node {
    int le,ri,k;
};
node d[110000];
int a[110000],b[110000],pos[110000],sum[110000],cnt,tot,n,m,k;
int L[110000],R[110000],f[110000],g[110000];
inline bool cmp(const node x,const node y){
    return x.le<y.le;
}
int main(){
    int i,j;
    scanf("%d%d%d",&n,&k,&m);
    for(i=1;i<=m;i++){
      scanf("%d%d%d",&d[i].le,&d[i].ri,&d[i].k);
      if(!d[i].k){
          sum[d[i].le]++;
          sum[d[i].ri+1]--;
      }
    }
    for(i=1;i<=n;i++){
      sum[i]+=sum[i-1];
      if(!sum[i]){
          pos[++tot]=i;
          a[i]=b[i]=tot;
      }
    }
    if(tot==k){
      for(i=1;i<=tot;i++)
        printf("%d
",pos[i]);
      return 0;
    }
    a[n+1]=n+1;
    for(i=n;i>0;i--)
      if(!a[i])a[i]=a[i+1];
    for(i=1;i<=n;i++)
      if(!b[i])b[i]=b[i-1];
    for(i=1;i<=m;i++)
      if(d[i].k){
          d[++cnt].le=a[d[i].le];
          d[cnt].ri=b[d[i].ri];
      }
    m=cnt;
    sort(d+1,d+m+1,cmp);
    cnt=0;
    for(i=1;i<=m;i++){
      while(cnt&&d[i].le>=L[cnt]&&d[i].ri<=R[cnt])--cnt;
      L[++cnt]=d[i].le;
      R[cnt]=d[i].ri;
    }
    int l=n+1,r=0;
    for(i=1;i<=cnt;i++){
      if(L[i]>r)f[i]=f[i-1]+1,r=R[i];
        else f[i]=f[i-1];
    }
    for(i=cnt;i>0;i--){
      if(R[i]<l)g[i]=g[i+1]+1,l=L[i];
        else g[i]=g[i+1];
    }
    int ok=0;
    for(i=1;i<=cnt;i++){
      if(f[i]==f[i-1])continue;
      if(L[i]==R[i]){
          printf("%d
",pos[R[i]]);
          ok=1;
          continue;
      }
      int l=0,r=cnt+1,x=0,y=cnt+1;
      while(r-l>1){
          int mid=(l+r)>>1;
          if(R[mid]<R[i]-1)x=mid,l=mid;
            else r=mid;
      }
      l=0,r=cnt+1;
      while(r-l>1){
          int mid=(l+r)>>1;
          if(L[mid]>R[i]-1)y=mid,r=mid;
            else l=mid;
      }
      if(f[x]+g[y]+1>k){
          printf("%d
",pos[R[i]]);
          ok=1;
      }
    }
    if(!ok)puts("-1");
    return 0;
}

以上是关于p3634 [APIO2012]守卫的主要内容,如果未能解决你的问题,请参考以下文章

「APIO2012」派遣

bzoj2809 [ APIO2012 ] -- 主席树

APIO2012 派遣

bzoj2806 [Apio2012]dispatching可并堆

P1552 [APIO2012]派遣

p1552 [APIO2012]派遣