博弈論——洛谷P6560 [SBCOI2020] 時光的流逝

からし發表於2024-05-27

[SBCOI2020] 時光的流逝

題目背景

時間一分一秒的過著,伴隨著雪一同消融在了這個冬天,
或許,要是時光能停留在這一刻,該有多好啊。
......
“這是...我在這個小鎮的最後一個冬天了吧。”
“嗯,你可不能這輩子都呆在這個小鎮吧。外面的世界很大呢,很大很大...”
“唔...外面的世界...突然有點期待呢!”
“總有一天,你會走得很遠很遠。以後你可不要忘記這個小鎮那。”
“不會的,至少...這裡曾經是我最快樂的一段回憶呢!你也一定不要忘記我呀。”
“你看,這雪花。傳說,每當世界上有一份思念,便會化成一片雪花在這裡飄落。”
“那...以後你可一定要找到我的那片雪花啊......”

“嗯,不如我們一起在這個冬天創造最後一段回憶吧。”
“好呀,我們玩個遊戲吧......”

題目描述

這個遊戲是在一個有向圖(不保證無環)上進行的。每輪遊戲開始前,她們先在圖上選定一個起點和一個終點,並在起點處放上一枚棋子。

然後兩人輪流移動棋子,每次可以將棋子按照有向圖的方向移動至相鄰的點。

如果誰先將棋子移動至終點,那麼誰就勝利了。同樣,如果誰無法移動了,那麼誰就失敗了。

兩人輪流操作,請問,他們是否有必勝策略呢?

答案為一個整數 01-1,其中 1 表示(先手)有必勝策略,-1 表示後手有必勝策略,0 表示兩人均無必勝策略。

輸入格式

\(\text{1}\)行有三個整數 \(n,m,q\) ,表示圖上有 \(n\) 個點, \(m\) 條邊,一共進行 \(q\) 輪遊戲。
接下來 \(m\) 行,每行輸入兩個數 \(u_i,v_i\) ,表示 \(u_i\)\(v_i\) 有一條邊。
接下來 \(q\) 行,每行兩個數 \(x,y\) ,表示每輪操作的起點和終點。資料保證起點,終點不同

輸出格式

對於每輪遊戲,僅輸出一個整數 01-1,其中 1 表示先手有必勝策略,-1 表示後手有必勝策略,0 表示兩人均無必勝策略。

樣例 #1

樣例輸入 #1

7 7 1
1 2
2 3
3 4
4 5
3 6
7 5
6 7
1 5

樣例輸出 #1

1

樣例 #2

樣例輸入 #2

5 5 2
1 2
2 3
3 1
3 4
4 5
1 5
4 3

樣例輸出 #2

0
1

提示

樣例解釋 \(#1\)

為描述題意,假設兩人為 A(先手)和 B

如圖,A 先走,走到 \(2\),B 走到 \(3\),接下去 A 可以選擇走到 \(4\)\(6\),若走到 \(4\),接下去 B 可以走到終點,故不可取。若選擇走到 \(6\),那麼 B 只能走到 \(7\),A 可以走到終點。所以 A 有必勝策略。

樣例解釋 \(#2\)

如圖,起點為 \(1\),終點為 \(5\) 時, A 和 B 會沿著 \(1-2-3-1\) 的順序輪流走。因為如果誰先走到 \(4\),那麼下一個人就可以走到終點。故誰都沒有必勝策略。

起點為 \(4\),終點為 \(3\) 時,A 先走到 \(5\),B 無路可走,故 B 失敗。

資料範圍

對於 \(10\%\) 的資料,保證圖是一條鏈。

對於 \(50\%\) 的資料,\(1\leq n\leq 10^3\)\(1\leq m\leq 2\times10^3\)\(1\leq q\leq 10\)

對於 \(70\%\) 的資料,\(1\leq n\leq 10^5\)\(1\leq m\leq 2\times10^5\)\(1\leq q\leq 10\)

對於 \(100\%\) 的資料,\(1\leq n\leq 10^5\)\(1\leq m\leq 5\times10^5\)\(1\leq q\leq 500\)

思路

首先考慮輸出是 \(0\) 的情況,對於當前的人必輸,所以控制對方呆在迴圈之中,此時的答案是 \(0\),起點如果在距離終點 \(1\) 的位置那麼先手必勝,所以假設終點處是必敗(不存在起點等於終點的情況),\(st[ed]\)\(-1\),如果有人無法移動,那麼這個人輸,在有向圖上無法移動,也就是出度為 \(0\),那麼先將出度為 \(0\) 的點和終點存入佇列,跑拓撲排序,如果上一個節點是必敗,那麼這個點無論是不是在環內,一定是必勝是狀態,如果上一個點是必勝的狀態,這個點不在環內的話就是必輸狀態,但如果這個點在環內,那麼他的狀態就不能現在確定,要透過其他的點來判斷或者 \(st[x]=0\)。至此,所有點的狀態都確定了,直接輸出就行,程式碼如下。
`

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, m, q;
vector<int> g[N];
int ru[N];
int st[N], now[N];

void solve()
{
   cin >> n >> m >> q;
   while (m --)
   {
      int a, b;
      cin >> a >> b;
      g[b].push_back(a);
      ru[a] ++;
   }
   while (q --)
   {
      int be, ed;
      cin >> be >> ed;
      queue<int> qu;
      for (int i = 1; i <= n; i ++)
      {
         now[i] = ru[i];
         if (!now[i] || i == ed) 
         {
            st[i] = -1;
            qu.push(i);
         }
         else st[i] = 0;
      }
      while (qu.size())
      {
         int it = qu.front();
         qu.pop();
         for (auto x : g[it])
         {
            if (!st[x])
            {
               now[x] --;
               if (st[it] == -1)
               {
                  st[x] = 1;
                  qu.push(x);
               }
               else if (!now[x])
               {
                  st[x] = -1;
                  qu.push(x);
               }
            }
         }
      }
      cout << st[be] << "\n";
   }
}

signed main()
{
   ios::sync_with_stdio(false);
   cin.tie(nullptr);
   cout.tie(nullptr);

   solve();
}

`