[ USACO 2013 OPEN ] Photo

$\$ios

Description


有一個長度爲 $n$ 的奶牛隊列,奶牛顏色爲黑或白。git

現給出 $m$ 個區間 $[L_i,R_i]$ ,要求:每一個區間裏 有且只有一隻黑牛優化

問知足全部給出限制最少有多少頭黑牛,若無合法方案輸出 $-1$ 。spa

  • $n\le 2\times 10^5,m\le 10^5$

$\$code

Solution


單調隊列優化。隊列

設 $f[i]$ 表示,第 $i$ 個位置爲黑牛, $[1,i]$ 的設置符合全部限制,最少有多少頭黑牛。ip

考慮合法的轉移區間的限制有哪些。get

  • 每一個區間裏只能有一頭黑牛。string

    這個限制說明,全部包含 $i$ 的區間裏,都不能再有黑牛,因此轉移區間右端點爲,包含 $i$ 的區間裏最小的的 $L_i-1$ 。it

  • 每一個區間裏必須有一頭黑牛。

    這個限制比較麻煩。由於不能有區間空着,因此全部不包含 $i$ 的區間裏都要有黑牛。

    因此咱們要找到,不包含 $i$ 的區間裏最大的 $L_i$,轉移的右端點就是這個 $L_i$ 。

而後就能夠單調隊列優化了。注意不合法狀態不放到單調隊列裏。

$\$

Code


寫起來其實仍是能夠判斷代碼能力的。

有一種比較優秀的寫法 不知道比我原來yy的高到哪裏去了 ,利用了一個單調性。

一個點轉移的合法區間左右端點其實都有單調性。

若是包含這個位置的最左端點要小於上一個位置,顯然上一個位置能夠直接換成這個值。

若是不包含這個位置的最右端點要小於上一個位置,顯然這個位置的右端點也能夠直接換成上一個位置的值。

這樣一來咱們的預處理是線性的了,也就是說,對於每一個區間,咱們只須要標記區間右端點和區間右端點 $+1$ 的位置,最後掃描一遍全部位置。

還有一個技巧是統計答案的時候,不須要討論最後一個位置什麼顏色,只須要讓數列長度 $+1$ , 最後一個位置的 $f$ 值就是答案。

#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 200010
#define R register
#define gc getchar
#define inf 2000000000
using namespace std;

inline int rd(){
  int x=0; bool f=0; char c=gc();
  while(!isdigit(c)){if(c=='-')f=1;c=gc();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
  return f?-x:x;
}

int n,m,l[N],r[N],f[N],q[N],hd,tl;

int main(){
  n=rd(); m=rd();
  for(R int i=1;i<=n+1;++i) r[i]=i-1;
  for(R int i=1,sl,sr;i<=m;++i){
    sl=rd(); sr=rd();
    r[sr]=min(r[sr],sl-1);
    l[sr+1]=max(l[sr+1],sl);
  }
  for(R int i=1;i<=n+1;++i) l[i]=max(l[i],l[i-1]);
  for(R int i=n;i;--i) r[i]=min(r[i],r[i+1]);
  int ptr=1; hd=tl=1;
  for(R int i=1;i<=n+1;++i){
    while(ptr<=r[i]){
      if(f[ptr]<0){++ptr;continue;}
      while(f[ptr]>f[q[tl]]&&hd<=tl) --tl;
      q[++tl]=ptr; ++ptr;
    }
    while(q[hd]<l[i]&&hd<=tl) ++hd;
    if(hd<=tl) f[i]=f[q[hd]]+(i!=n+1);
    else f[i]=-1;
  }
  printf("%d\n",f[n+1]);
  return 0;
}
相關文章
相關標籤/搜索