Problem:
Problem J:
ねこ泥棒と金曜日のお屋敷
なつめは大のねこ好きである。なつめの通学路には通称ねこ屋敷と呼ばれている家がある。その家はたくさんのねこを飼っていることで有名で、なつめは通学途中によくこの家の前で飼いねこに遭遇し、一緒に遊んでいた。そんなある日、なつめは衝撃的な事実を知ってしまった。それは、実はねこ屋敷の主人は、機嫌が悪くなるとよく飼いねこたちを虐待している、ということだった。ねこ屋敷の主人を許せなくなったなつめは、ねこたちを救うため、主人の居ない間にねこたちを盗みだすことにした。
なつめはねこ屋敷の主人の行動パターンを観察し、毎週決まって出掛けているタイミングをねらってねこたちを盗みだすことにした。ねこ屋敷は二次元平面として表わされており、なつめが屋敷の中に忍びこむ時点でのそれぞれのねこの位置は分かっている。ねこたちはそれぞれ決まったルートを常に50メートル毎分のスピードでまわっている。一方、なつめは最大80メートル毎分のスピードで移動できる。なつめは屋敷のとある場所から侵入し、屋敷内を移動し、主人が帰ってくるまでに脱出口から出る。なつめがねこと同じ地点に到達すると、なつめはねこを抱えることができる。これを行なうのにかかる時間は無視できる。なつめは何匹でもねこを抱えることができるし、何匹抱えていても移動速度が落ちることはないが、必ず主人が帰ってくる前に屋敷を脱出しなければならない。
残念ながら、なつめはねこ屋敷のねこを全員盗みだすことはできないかもしれない。しかし、1匹でも多くのねこを幸せにするために、できるだけ多くのねこを盗みだすことにした。また、同じ数のねこを盗みだせるのであれば、屋敷の主人に捕まるリスクを抑えるため、できるだけ早い時間に屋敷から脱出する。
なつめが屋敷に侵入する時刻と位置、屋敷の主人が戻ってくる時刻、脱出口の場所、およびねこの初期位置と巡回ルートが与えられる。なつめが何匹のねこを盗みだして、どの時刻に屋敷を脱出できるかを答えるプログラムを書いてほしい。
Input
入力の1行目には、侵入口の
x
,
y
座標が1つの空白文字で区切られて与えられる。2行目には同様に脱出口の位置が与えられる。3行目にはなつめが屋敷に侵入した時刻、4行目には屋敷の主人が帰ってくる時刻が24時間制のHH:MM:SSの形式で与えられる。
5行目はねこの総数
m
であり、続く
m
行に各ねこの行動パターンが与えられる。5+
i
行目 (
i
= 1, 2, ...,
m
) が
i
番目のねこの巡回ルートに対応している。各行の初めには自然数
k
i
が与えられ、続いてねこの巡回ルートが
k
i
個の
x
,
y
座標値を並べたものとして与えられる。ねこの巡回ルートは、これらの連続する点および最後と最初の点を線分でつないだものである。なつめが屋敷に侵入した時点で、ねこは与えられた最初の点におり、以降ずっと巡回ルート上を等速で移動する。与えられたルートの連続する点が同じ座標であることはない。
なお、スタート時間とゴール時間は同じ日でのものであることが保証されている．なつめが主人の帰ってくる前に脱出する方法は必ず存在する。同じ行にある数は全て1つの空白文字で区切られている。
ねこの数は1以上14以下、ねこの巡回ルートを表わす点の数は2以上1000以下であり、全ての座標値は絶対値が100000を越えない整数であることが保証されている。
座標値の単位は全てメートルである。
Output
1行目に、なつめが盗み出すことのできるねこの最大数を出力せよ。2行目には、なつめがなるべく多い数のねこを盗みだす方法の中で、最も早く脱出口に到達できる時刻を、HH MM SS.nnnnnn (空白区切り)の形式で答えよ。秒の小数点以下は6桁出力せよ。なお、10
-6
秒を越える誤差があってはならない。
なお、主人の帰ってくる時間が±1ms変化しても、最多遭遇可能数は変化しないことが保証されている。
Notes on Submission
上記形式で複数のデータセットが与えられます。入力データの 1 行目にデータセットの数が与えられます。各データセットに対する出力を上記形式で順番に出力するプログラムを作成して下さい。
Sample Input
2
0 0
0 0
15:00:00
18:00:00
1
4 0 7199 1125 7199 1125 8324 0 8324
0 0
0 0
15:00:00
18:00:00
1
4 0 7201 1125 7201 1125 8326 0 8326
Output for the Sample Input
1
17 59 59.076923
0
15 00 00.000000

Submitted solutions:

code_snippet: #include <iostream>
#include <cstdio>
#include <iomanip>
#include <complex>
#include <vector>
#include <algorithm>
#include <cmath>
#include <utility>
using namespace std;
const double EPS = 1e-8;
const double INF = 1e12;
#define EQ(n,m) (abs((n)-(m)) < EPS)
#define X real()
#define Y imag()
typedef complex<double> P;
typedef vector<P> VP;

//ループにおいてcが区間[a,b]に入るか
bool in_range(double a, double b, double c){
    if(a < b) return a<=c && c<=b;
    return c<b || a<c;
}

const double vn = 80.0/60.0; //velocity of natsume (m/s)
const double vc = 50.0/60.0; //velocity of cat (m/s)

//出発座標と現在時刻、巡回ルートと累積和を与え、
//捕まえる座標と時刻を返す
pair<P, double> fastest_catch(P s, double time, VP &route, vector<double> &sumtime){
    int n = route.size();
    //前・後の切れ目になる時刻
    double divtime = time +abs(s -route[0])/vn +sumtime.back()/2;
    divtime = fmod(divtime, sumtime.back());
    
    //二分探索でどの頂点間かを求める
    int sidx, lb=0, ub;
    auto itr = lower_bound(sumtime.begin(), sumtime.end(), divtime);
    if(EQ(*itr, divtime)){
        sidx = itr -sumtime.begin();
        ub = n;
    }else{
        sidx = (itr -sumtime.begin() +n-1) %n;
        ub = n+1;
    }
    int count = 0;
    while(ub -lb > 1){
        if(count++ >10) break;
        int mid = (ub +lb)/2;
        int mididx = (sidx +mid) %n;
        double t = time +abs(s -route[mididx])/vn;
        t = fmod(t, sumtime.back());
        if(in_range(divtime, sumtime[mididx], t)){
            ub = mid;
        }else{
            lb = mid;
        }
    }

    P a=route[(sidx+lb)%n], b=route[(sidx+ub)%n];
    P ab = (b-a)/abs(b-a);
    double dlb=0, dub=abs(a-b);
    for(int rep=0; rep<40; rep++){
        double mid = (dlb +dub) /2.0;
        P midP = a +mid*ab;
        double t = time +abs(s -midP)/vn;
        t = fmod(t, sumtime.back());
        if(in_range(divtime, fmod(sumtime[(sidx+lb)%n] +mid/vc, sumtime.back()), t)){
            dub = mid;
        }else{
            dlb = mid;
        }
    }
    P midP = a +dlb*ab;
    return make_pair(midP, time +abs(s -midP)/vn);
}

int bitcount(int n){
    n = (n & 0x55555555) + (n >> 1 & 0x55555555);
    n = (n & 0x33333333) + (n >> 2 & 0x33333333);
    n = (n & 0x0f0f0f0f) + (n >> 4 & 0x0f0f0f0f);
    n = (n & 0x00ff00ff) + (n >> 8 & 0x00ff00ff);
    return (n & 0x0000ffff) + (n >>16 & 0x0000ffff);
}

int main(){
    int dataset;
    cin >> dataset;
    for(int rep=0; rep<dataset; rep++){
        P s,g;
        {
            int sx,sy,gx,gy;
            cin >> sx >> sy >> gx >> gy;
            s = P(sx, sy);
            g = P(gx, gy);
        }
        int begintime,endtime;
        {
            int hh,mm,ss;
            scanf("%d:%d:%d", &hh, &mm, &ss);
            begintime = hh*3600 +mm*60 +ss;
            scanf("%d:%d:%d", &hh, &mm, &ss);
            endtime = hh*3600 +mm*60 +ss;
        }
        double tlimit = endtime -begintime;

        int n;
        cin >> n;
        //巡回ルート
        vector<VP> route(n);
        for(int i=0; i<n; i++){
            int size;
            cin >> size;
            route[i].resize(size);
            for(int j=0; j<size; j++){
                int x,y;
                cin >> x >> y;
                route[i][j] = P(x, y);
            }
        }
        //各頂点までにかかる時間の累積和
        vector<vector<double> > sumtime(n);
        for(int i=0; i<n; i++){
            int size = route[i].size();
            sumtime[i].resize(size +1);
            sumtime[i][0] = 0;
            for(int j=0; j<size; j++){
                sumtime[i][j+1] = sumtime[i][j] + abs(route[i][j] -route[i][(j+1)%size]) /vc;
            }
        }

        //dp[i][j] = (集合iを確保,最後にjをとったときの最短時刻)
        vector<vector<double> > dp(1<<n, vector<double>(n, INF));
        vector<vector<P> > pos(1<<n, vector<P>(n));
        for(int i=0; i<n; i++){
            auto ret = fastest_catch(s, 0, route[i], sumtime[i]);
            pos[1<<i][i] = ret.first;
            dp[1<<i][i] = ret.second;
        }

        int maxnum = 0;
        double mintime = abs(s -g) /vn;
        for(int i=1; i<(1<<n); i++){
            for(int j=0; j<n; j++){
                if(dp[i][j] == INF) continue;
                //ここから出口に直行しても間に合わないなら打ち切り
                double time = dp[i][j] +abs(pos[i][j] -g)/vn;
                if(time > tlimit) continue;
                //スコア更新(なるべく多く、なるべく早く)
                if(bitcount(i) == maxnum && time < mintime){
                    mintime = time;
                }else if(bitcount(i) > maxnum){
                    maxnum = bitcount(i);
                    mintime = time;
                }
                
                for(int k=0; k<n; k++){
                    if(i>>k & 1) continue;
                    auto ret = fastest_catch(pos[i][j], dp[i][j], route[k], sumtime[k]);
                    if(ret.second < dp[i|(1<<k)][k]){
                        pos[i|(1<<k)][k] = ret.first;
                        dp[i|(1<<k)][k] = ret.second;
                    }
                }
            }
        }

        int integer_part = begintime +mintime +EPS;
        double fractional_part = begintime +mintime -integer_part;
        int hh = integer_part /3600;
        integer_part %= 3600;
        int mm = integer_part /60;
        double ss = integer_part %60 +fractional_part;
        printf("%d\n", maxnum);
        printf("%02d %02d %09.6f\n", hh, mm, ss);
    }
    return 0;
}
accuracy: 1
time_ms: 5260
memory_kb: 10132
final_rank: 1

code_snippet: #include <iostream>
#include <cstdio>
#include <iomanip>
#include <complex>
#include <vector>
#include <algorithm>
#include <cmath>
#include <utility>
using namespace std;
const double EPS = 1e-8;
const double INF = 1e12;
#define EQ(n,m) (abs((n)-(m)) < EPS)
#define X real()
#define Y imag()
typedef complex<double> P;
typedef vector<P> VP;

//ループにおいてcが区間[a,b]に入るか
bool in_range(double a, double b, double c){
    if(a < b) return a<=c && c<=b;
    return c<b || a<c;
}

const double vn = 80.0/60.0; //velocity of natsume (m/s)
const double vc = 50.0/60.0; //velocity of cat (m/s)

//出発座標と現在時刻、巡回ルートと累積和を与え、
//捕まえる座標と時刻を返す
pair<P, double> fastest_catch(P s, double time, VP &route, vector<double> &sumtime){
    int n = route.size();
    //前・後の切れ目になる時刻
    double divtime = time +abs(s -route[0])/vn +sumtime.back()/2;
    divtime = fmod(divtime, sumtime.back());
    
    //二分探索でどの頂点間かを求める
    int sidx, lb=0, ub;
    auto itr = lower_bound(sumtime.begin(), sumtime.end(), divtime);
    if(EQ(*itr, divtime)){
        sidx = itr -sumtime.begin();
        ub = n;
    }else{
        sidx = (itr -sumtime.begin() +n-1) %n;
        ub = n+1;
    }
    int count = 0;
    while(ub -lb > 1){
        if(count++ >10) break;
        int mid = (ub +lb)/2;
        int mididx = (sidx +mid) %n;
        double t = time +abs(s -route[mididx])/vn;
        t = fmod(t, sumtime.back());
        if(in_range(divtime, sumtime[mididx], t)){
            ub = mid;
        }else{
            lb = mid;
        }
    }

    P a=route[(sidx+lb)%n], b=route[(sidx+ub)%n];
    P ab = (b-a)/abs(b-a);
    double dlb=0, dub=abs(a-b);
    for(int rep=0; rep<50; rep++){
        double mid = (dlb +dub) /2.0;
        P midP = a +mid*ab;
        double t = time +abs(s -midP)/vn;
        t = fmod(t, sumtime.back());
        if(in_range(divtime, fmod(sumtime[(sidx+lb)%n] +mid/vc, sumtime.back()), t)){
            dub = mid;
        }else{
            dlb = mid;
        }
    }
    P midP = a +dlb*ab;
    return make_pair(midP, time +abs(s -midP)/vn);
}

int bitcount(int n){
    n = (n & 0x55555555) + (n >> 1 & 0x55555555);
    n = (n & 0x33333333) + (n >> 2 & 0x33333333);
    n = (n & 0x0f0f0f0f) + (n >> 4 & 0x0f0f0f0f);
    n = (n & 0x00ff00ff) + (n >> 8 & 0x00ff00ff);
    return (n & 0x0000ffff) + (n >>16 & 0x0000ffff);
}

int main(){
    int dataset;
    cin >> dataset;
    for(int rep=0; rep<dataset; rep++){
        P s,g;
        {
            int sx,sy,gx,gy;
            cin >> sx >> sy >> gx >> gy;
            s = P(sx, sy);
            g = P(gx, gy);
        }
        int begintime,endtime;
        {
            int hh,mm,ss;
            scanf("%d:%d:%d", &hh, &mm, &ss);
            begintime = hh*3600 +mm*60 +ss;
            scanf("%d:%d:%d", &hh, &mm, &ss);
            endtime = hh*3600 +mm*60 +ss;
        }
        double tlimit = endtime -begintime;

        int n;
        cin >> n;
        //巡回ルート
        vector<VP> route(n);
        for(int i=0; i<n; i++){
            int size;
            cin >> size;
            route[i].resize(size);
            for(int j=0; j<size; j++){
                int x,y;
                cin >> x >> y;
                route[i][j] = P(x, y);
            }
        }
        //各頂点までにかかる時間の累積和
        vector<vector<double> > sumtime(n);
        for(int i=0; i<n; i++){
            int size = route[i].size();
            sumtime[i].resize(size +1);
            sumtime[i][0] = 0;
            for(int j=0; j<size; j++){
                sumtime[i][j+1] = sumtime[i][j] + abs(route[i][j] -route[i][(j+1)%size]) /vc;
            }
        }

        //dp[i][j] = (集合iを確保,最後にjをとったときの最短時刻)
        vector<vector<double> > dp(1<<n, vector<double>(n, INF));
        vector<vector<P> > pos(1<<n, vector<P>(n));
        for(int i=0; i<n; i++){
            auto ret = fastest_catch(s, 0, route[i], sumtime[i]);
            pos[1<<i][i] = ret.first;
            dp[1<<i][i] = ret.second;
        }

        int maxnum = 0;
        double mintime = abs(s -g) /vn;
        for(int i=1; i<(1<<n); i++){
            for(int j=0; j<n; j++){
                if(dp[i][j] == INF) continue;
                //ここから出口に直行しても間に合わないなら打ち切り
                double time = dp[i][j] +abs(pos[i][j] -g)/vn;
                if(time > tlimit) continue;
                //スコア更新(なるべく多く、なるべく早く)
                if(bitcount(i) == maxnum && time < mintime){
                    mintime = time;
                }else if(bitcount(i) > maxnum){
                    maxnum = bitcount(i);
                    mintime = time;
                }
                
                for(int k=0; k<n; k++){
                    if(i>>k & 1) continue;
                    auto ret = fastest_catch(pos[i][j], dp[i][j], route[k], sumtime[k]);
                    if(ret.second < dp[i|(1<<k)][k]){
                        pos[i|(1<<k)][k] = ret.first;
                        dp[i|(1<<k)][k] = ret.second;
                    }
                }
            }
        }

        int integer_part = begintime +mintime +EPS;
        double fractional_part = begintime +mintime -integer_part;
        int hh = integer_part /3600;
        integer_part %= 3600;
        int mm = integer_part /60;
        double ss = integer_part %60 +fractional_part;
        printf("%d\n", maxnum);
        printf("%02d %02d %09.6f\n", hh, mm, ss);
    }
    return 0;
}
accuracy: 1
time_ms: 6480
memory_kb: 10092
final_rank: 2

code_snippet: #include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <math.h>
#include <assert.h>
#include <vector>
#include <queue>
#include <string>
#include <map>
#include <set>

using namespace std;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
static const double EPS = 1e-9;
static const double PI = acos(-1.0);

#define REP(i, n) for (int i = 0; i < (int)(n); i++)
#define FOR(i, s, n) for (int i = (s); i < (int)(n); i++)
#define FOREQ(i, s, n) for (int i = (s); i <= (int)(n); i++)
#define FORIT(it, c) for (__typeof((c).begin())it = (c).begin(); it != (c).end(); it++)
#define MEMSET(v, h) memset((v), h, sizeof(v))

#include <complex>
typedef complex<double> Point;

int HtoS(int h, int m, int s) {
  return h * 3600 + m * 60 + s;
}
string StoH(double sec) {
  int h = (int)sec / 3600;
  int m = (int)sec % 3600 / 60;
  double ms =  sec - (h * 3600 + m * 60);
  char str[1000];
  sprintf(str, "%02d %02d %02d.%08d", h, m, (int)ms, (int)((ms - (int)ms) * 100000000 + 0.5));
  return str;
}

struct State {
  int use;
  double t;
  Point p;
  int last;
  State() {;}
  State(int use, double t, Point p, int last) : use(use), t(t), p(p), last(last) {;}
  bool operator<(const State &rhs) const {
    return t > rhs.t;
  }
};

const double SPEED_N = 80.0 / 60.0;
const double SPEED_C = 50.0 / 60.0;
Point sp, ep;
int startTime;
int endTime;
int n;
vector<Point> route[20];
vector<double> ts[20];

int bisect(State state, int target) {
  int lower = 0;
  int upper = 1 << 20;
  while (lower != upper) {
    int mid = (lower + upper) / 2;
    int cycle = mid / ts[target].size();
    int index = mid % ts[target].size();
    double et = cycle * ts[target].back() + ts[target][index] + startTime;
    Point p = route[target][(index + 1) % route[target].size()];
    if (abs(state.p - p) / SPEED_N - (et - state.t) <= -EPS) {
      upper = mid;
    } else {
      lower = mid + 1;
      if (et >= endTime) { break; }
    }
  }
  return lower;
}

pair<double, Point> calc(State state, int target) {
  int from = bisect(state, target);
  double lower;
  double upper;
  {
    int cycle = from / ts[target].size();
    int index = from % ts[target].size();
    lower = cycle * ts[target].back() + startTime;
    if (index != 0) {
      lower += ts[target][index - 1];
    }
    upper = cycle * ts[target].back() + ts[target][index] + startTime + EPS * 10;
  }
  double stime = lower;
  pair<double, Point> ret(endTime + 1, Point(0, 0));
  if (lower > endTime) { return ret; }
  from %= ts[target].size();
  Point vect = route[target][(from + 1) % route[target].size()] - route[target][from];
  vect /= abs(vect);
  bool ok = false;
  REP(i, 50) {
    double mid = (lower + upper) / 2.0;
    double t = mid - stime;
    Point tp = route[target][from] + vect * (t * SPEED_C);
    if (abs(state.p - tp) / SPEED_N <= mid - state.t) {
      upper = mid;
      ret = make_pair(mid, tp);
      ok = true;
    } else {
      lower = mid;
    }
  }
  double t = lower - stime;
  Point tp = route[target][from] + vect * (t * SPEED_C);
  //cout << abs(state.p - tp) / SPEED_N << " " << lower - state.t << endl;
  assert(ok);
  return ret;
}

bool visit[1 << 14][15];
double times[1 << 14][15];
int main() {
  int test;
  scanf("%d", &test);
  double sx, sy, ex, ey;
  while (scanf("%lf %lf %lf %lf", &sx, &sy, &ex, &ey) > 0) {
    sp = Point(sx, sy);
    ep = Point(ex, ey);
    {
      int h, m, s;
      scanf("%d:%d:%d", &h, &m, &s);
      startTime = HtoS(h, m, s);
      scanf("%d:%d:%d", &h, &m, &s);
      endTime = HtoS(h, m, s);
    }
    scanf("%d", &n);
    REP(i, n) {
      route[i].clear();
      ts[i].clear();
      int k;
      scanf("%d", &k);
      REP(j, k) {
        int x, y;
        scanf("%d %d", &x, &y);
        route[i].push_back(Point(x, y));
      }
      double pt = 0.0;
      REP(j, k) {
        ts[i].push_back(pt + abs(route[i][j] - route[i][(j + 1) % route[i].size()]) / SPEED_C);
        pt = ts[i].back();
      }
    }

    REP(i, 1 << 14) REP(j, 15) { times[i][j] = endTime + EPS * 2; }
    MEMSET(visit, false);
    priority_queue<State> que;
    que.push(State(0, startTime, sp, 14));
    pair<int, double> ans(-1, -1e+100);
    while (!que.empty()) {
      State state = que.top();
      que.pop();
      if (visit[state.use][state.last]) { continue; }
      //cout << state.use << " " << state.last << " "<< state.t << endl;
      visit[state.use][state.last] = true;
      {
        double et = state.t + abs(state.p - ep) / SPEED_N;
        if (et > endTime) { continue; }
        pair<int, double> lans(__builtin_popcount(state.use), -et);
        ans = max(ans, lans);
      }
      REP(i, n) {
        if ((state.use >> i) & 1) { continue; }
        int nuse = state.use | (1 << i);
        if (visit[nuse][i]) { continue; }
        pair<double, Point> next = calc(state, i);
        if (next.first - times[nuse][i] > EPS) { continue; }
        times[nuse][i] = next.first;
        que.push(State(nuse, next.first, next.second, i));
      }
    }
    printf("%d\n%s\n", ans.first, StoH(-ans.second).c_str());
  }
}
accuracy: 1
time_ms: 7920
memory_kb: 13000
final_rank: 3