ferin blog

Aug 31, 2018 - 3 minute read - 競技プログラミング

Codeforces Round #467 (Div. 2) D. Sleepy Game

問題ページ

考察をすると奇数回の遷移で出次数が0の頂点にたどりつくことができるか?という問題に帰着できる。d[i][j]=(頂点iにj(偶数回or奇数回)の遷移でたどり着くことができるかのbool) としてBFSを行う。d[s][1(偶数長)]=trueとしてBFSを開始し、BFSの結果d[g(出次数が0の頂点)][0(奇数長)]=trueであれば結果はWinとなる。Winの場合経路復元を行う必要があるためprev[i][j]=(頂点iに偶数回or奇数回の遷移でたどり着く前の頂点)を覚えておき経路復元を行う。
WinでなければDrawにできるかLoseかの判定を行うことになる。Drawにできる条件は閉路が存在することである。閉路検出はトポロジカルソートを用いて行うことができる。ここで注意するケースとして頂点sからつながっていないところにのみ閉路が存在するケースがある。頂点sから閉路にたどり着くことができなければ当然Drawにすることはできない。
WinでもDrawでもなければLoseを出力する。

経路復元で頂点番号がsと一致するまでしかループを回していなくてsを二回踏むパターンでバグらせたので拡張dijkstraの復元するときは気をつける。

BFSの状態の持ち方はJAG夏合宿2017F、DrawのコーナーケースはABC061Dを解いた経験を元に思いついた。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <bits/stdc++.h>

using namespace std;
using ll = long long;
#define int ll
using PII = pair<int, int>;
template <typename T> using V = vector<T>;
template <typename T> using VV = vector<V<T>>;
template <typename T> using VVV = vector<VV<T>>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()
#define PB push_back

const ll INF = (1LL<<60);
const int MOD = 1000000007;

template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template <typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template <typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; }
template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); }
template<class S,class T>
ostream &operator <<(ostream& out,const pair<S,T>& a){
  out<<'('<<a.first<<','<<a.second<<')';
  return out;
}
template<class T>
ostream &operator <<(ostream& out,const vector<T>& a){
  out<<'[';
  REP(i, a.size()) {out<<a[i];if(i!=a.size()-1)out<<',';}
  out<<']';
  return out;
}

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};

V<int> tsort(VV<int> g) {
  const int n = g.size();
  V<int> h(n, 0);
  REP(i, n) for(int j: g[i]) h[j]++;

  stack<int> st;
  REP(i, n) if(h[i] == 0) st.push(i);

  V<int> ans;
  while(st.size()) {
    int i = st.top(); st.pop();
    ans.push_back(i);
    for(auto& j: g[i]) {
      h[j]--;
      if(h[j] == 0) st.push(j);
    }
  }

  return ans;
}

signed main(void)
{
  cin.tie(0);
  ios::sync_with_stdio(false);

  int n, m;
  cin >> n >> m;
  VV<int> g(n);
  REP(i, n) {
    int c;
    cin >> c;
    REP(j, c) {
      int x;
      cin >> x;
      x--;
      g[i].PB(x);
    }
  }
  int s;
  cin >> s;
  s--;

  VV<int> d(n, V<int>(2, 0));
  VV<int> prev(n, V<int>(2, -1));
  d[s][1] = 1;
  queue<PII> que;
  que.push({s, 1});

  while(que.size()) {
    PII p = que.front(); que.pop();
    for(auto i: g[p.first]) {
      if(d[i][!p.second] == 0) {
        d[i][!p.second] = d[p.first][p.second] + 1;
        que.push({i, !p.second});
        prev[i][!p.second] = p.first;
      }
    }
  }

  V<int> goal;
  REP(i, n) if(g[i].size()==0) goal.PB(i);

  for(auto i: goal) if(d[i][0] != 0 && d[i][0]-1 < 1e6) {
    cout << "Win" << endl;
    V<int> path;
    int now = i, turn = 0;
    while(now != s || turn != 1) {
      path.PB(now);
      now = prev[now][turn];
      turn = !turn;
    }
    path.PB(s);
    reverse(ALL(path));
    for(auto i: path) cout << i+1 << " ";
    cout << endl;
    return 0;
  }
  
  VV<int> h(n);
  REP(i, n) {
    if(d[i][0]==0 && d[i][1]==0) continue;
    for(auto j: g[i]) {
      if(d[j][0]==0 && d[j][1]==0) continue;
      h[i].PB(j);
    }
  }

  V<int> ret = tsort(h);
  if(ret.size() != n) cout << "Draw" << endl;
  else cout << "Lose" << endl; 

  return 0;
}