土下座しながら探索中

主に競技プログラミング

ICPC 2014 WF ( UVa 1707 ) Surveillance

問題リンク : http://uva.onlinejudge.org/external/17/1707.pdf

問題概要 :
凸多角形の建物がある
それはn枚の壁からなり、壁は1からnまで番号がつけられている
k個のカメラがその建物のまわりに設置してあり、i番目のカメラはs[i]からt[i]までの壁を撮影できる
s[i] <= t[i] のとき、カメラは壁j ( s[i] <= j <= t[i] )をすべて撮影する
s[i] > t[i] のとき、カメラは壁j ( s[i] <= n, 1 <= t[i] )をすべて撮影する
このとき、1からnまで全て撮影するために必要なカメラは何個か?

解法:
蟻本にのってると話題になってアレ
蟻本第二版のP307、巡回スケジューリングと同様の解法で解く
スタートのカメラを決めたとき、その後は貪欲で求まる
但し、貪欲を愚直にやってるとTLEするのでダブリングをして二分探索することでlognで解を求める
壁をそのまま扱うとやりづらいのでそれぞれを2倍して循環を消した後に各sをマイナス1してセグメントに変換した

コード :

#include<bits/stdc++.h>

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)

using namespace std;

typedef long long ll;
typedef pair<int,int> ii;

const int IINF = INT_MAX;
const int MAX_K = 1000100;
const int MAX_LOG = 30;

int n,k,s[MAX_K*2],t[MAX_K*2],Next[MAX_LOG][MAX_K*2];
ii ps[MAX_K*4];

inline void compute(){

  rep(i,k){
    if( t[i] < s[i] ) t[i] += n;
    s[i+k] = s[i] + n;
    t[i+k] = t[i] + n;
    --s[i],--s[i+k];
  }
  rep(i,2*k) {
    ps[i]     = ii(s[i],i);
    ps[2*k+i] = ii(t[i],2*k+i);
  }
  sort(ps,ps+4*k);

  int last = -1;
  rep(i,4*k){
    int id = ps[i].second;
    if( id < 2*k ) {
      if( last < 0 || ( t[last] < t[id] ) ) {
        last = id;
      }
    } else {
      if( t[last] == t[id-2*k] ) last = -1;
      Next[0][id-2*k] = last;
    }
  }

  for(int i=0;i+1<MAX_LOG;i++){
    rep(j,2*k){
      if( Next[i][j] < 0 ) Next[i+1][j] = -1;
      else                 Next[i+1][j] = Next[i][Next[i][j]];
    }
  }
  int ans = IINF;
  rep(i,k){
    int tmp = 0, j = i;
    for(int lg=MAX_LOG-1;lg>=0;lg--){
      int j2 = Next[lg][j];
      if( t[j] >= s[i]+n ) break;
      if( j2 >= 0 && s[j2] < s[i]+n ){
        j = j2;
        tmp |= (1<<lg);
      }
    }
    if( t[j] >= s[i]+n )ans = min(ans,tmp+1);

  }
  if( ans == IINF ) puts("impossible");
  else              printf("%d\n",ans);
}

int main(){
  while( scanf("%d %d",&n,&k) != EOF ){
    rep(i,k) scanf("%d %d",s+i,t+i);
    compute();
  }
  return 0;
}