Skip to content
Logic Formatting

Logic Formatting

E. The Road to Berland is Paved With Good Intentions 解析(思維、2-SAT、SCC、拓樸排序)

Posted on 2025 年 2 月 10 日2025 年 2 月 12 日 By jeff

今天我們來看看CF228E 題目連結

題目描述

給定一個包含 $n$ 個城市和 $m$ 條道路的無向圖,每條道路都有一個初始狀態:

  • 0 表示該道路尚未鋪設;
  • 1 表示該道路已鋪設。

每天,國王 Valera II 可以選擇 恰好一個城市,並指派工人對該城市相連的所有道路進行取反操作(即將 0 變為 1,1 變為 0)。
由於對同一城市重複操作沒有額外效果(取反兩次恢復原狀),所以每個城市最多只需操作一次。
你的任務是判斷是否存在一個操作序列(操作天數不超過 $n$ 天),使得最終所有道路均變為鋪設狀態(即狀態均為 1)。若存在,請輸出一組合法的操作方案;否則,輸出 Impossible。


輸入格式

  • 第一行包含兩個整數 $n$ 和 $m$,分別代表城市數量和道路數量。
  • 接下來 $m$ 行,每行包含三個整數 $a_i, b_i, c_i$:
    • $a_i$ 和 $b_i$ 表示一條連接這兩個城市的道路(保證 $a_i \neq b_i$);
    • $c_i$ 為該道路的初始狀態($0$ 表示未鋪設,$1$ 表示已鋪設)。

輸出格式

  • 如果存在合法操作序列:
    • 第一行輸出操作天數 $x$(滿足 $0 \le x \le n$);
    • 第二行輸出 $x$ 個整數,依次表示每天選擇操作的城市編號。
  • 如果不存在合法方案,則輸出 Impossible。

前言

想不出來的時候還是多看解答好了,以免浪費時間在我沒學過的演算法上面

想法

這題看起來有三種解法:

  1. 高斯消去法
  2. 2-SAT
  3. Greedy

這次我使用第二種,2-SAT的方法。剛好把以前沒學懂的BCC、SCC、DFS回邊、前向邊、樹邊、交錯邊學起來。
這題對我來說就是2-SAT的模板題:
開一張新的圖,如果原圖中$x,y$的連線是$0$,那麼邏輯關係就是:
\begin{align*} &x\rightarrow\neg y \ &\neg x\rightarrow y \ &\neg y\rightarrow x \ &y\rightarrow\neg x \end{align*} 反之如果原圖中$x,y$的連線是$1$,那麼邏輯關係就是:
\begin{align*} &x\rightarrow y \ &\neg x\rightarrow\neg y \ &\neg y\rightarrow\neg x \ &y\rightarrow x \end{align*} 而我們新的圖裡面可以用$x*2$當成False的點,$x*2+1$當成True的點,用這個規則造一張新的,包含$2n$個節點的新圖。
接著使用Tarjan’s Algorithm找SCC。
Tarjan找BCC和找SCC除了無向圖和有向圖的差異以外,就在

if (!scc[u]) low[v] = min(low[v], low[u]);

這一行而已。
如果要找BCC,我們的dfs函數就需要有int father這個參數,判斷接下來要dfs的點是不是父節點(有向圖裡面往父節點的邊也是合理的回邊)。並且由於無向圖沒有交錯邊,因此不需要判斷!scc[u]
注意到Tarjan SCC裡面,我們指派SCC Number是在dfs的後序的地方。而回憶起拓樸排序的其中一種作法就是紀錄dfs的後序,就是反過來的拓樸順序了。
而SCC縮點以後,會是一個有向無環圖(DAG),因此SCC Number小的SCC,會在拓樸排序的後面。
當然,我們要先確認有沒有邏輯矛盾,也就是會不會有$x\rightarrow\neg x$的情況發生,也就是$x$和$\neg x$的節點在圖的同一個SCC中。
再來我們就可以從後面開始指派布林變數的值了。因為如果我們從後面開始指派,就不會有邏輯關係可以卡到我們了,例如,$x\rightarrow y$在SCC Number=2的SCC中,而$\neg x\rightarrow\neg y$在SCC Number=1的SCC中。
如果我們先指定$y=true$,因為SCC=2會有邏輯關係指向SCC=1,因此就會發生$y\rightarrow…\rightarrow\neg x\rightarrow\neg y$,發生矛盾。
因此我們必須指定$\neg y=true$,也就是$y=false$。如此一來前面的 $$ y\rightarrow…\rightarrow\neg x\rightarrow\neg y $$ 就會因為前提$y$根本不成立,而不會有矛盾。
(這個說明方法我也不是那麼確定是不是對的,如果有人知道正確的講法,還請賜教)

程式碼:

const int _n = 100 + 10;
int t, n, m, T = 0;
VI G[_n * 2];
int id[_n * 2], low[_n * 2], scc[_n * 2];
stack<int> st;
void dfs(int v) {        // Tarjan SCC
  id[v] = low[v] = ++T;  // 前序
  st.push(v);
  for (int u : G[v]) {
    if (!id[u]) dfs(u);
    if (!scc[u]) low[v] = min(low[v], low[u]);  // 如果是要求BCC 就要判斷u!=father 然後不用判斷!scc[u]
  }
  if (low[v] == id[v]) {
    int temp;
    do {
      scc[temp = st.top()] = v;  // SCC是dfs的後序,剛好是縮點後的DAG的拓樸排序
      st.pop();
    } while (temp != v);
  }
}
main(void) {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  cin >> n >> m;
  rep(i, 0, m) {
    int a, b, c;
    cin >> a >> b >> c;
    a--, b--;  // 這跟題目有關 題目的vertex是1-indexed
    if (c) {
      G[a * 2 + 1].pb(b * 2 + 1);  // x*2 false, x*2+1 true
      G[b * 2 + 1].pb(a * 2 + 1);
      G[a * 2].pb(b * 2);
      G[b * 2].pb(a * 2);
    } else {
      G[a * 2 + 1].pb(b * 2);
      G[b * 2 + 1].pb(a * 2);
      G[b * 2].pb(a * 2 + 1);
      G[a * 2].pb(b * 2 + 1);
    }
  }
  rep(i, 0, n) {
    if (!id[i * 2]) dfs(i * 2);
    if (!id[i * 2 + 1]) dfs(i * 2 + 1);
  }
  // tarjan 算出來的scc 實際上是反過來的topo order
  // 而這剛好是我們找2-SAT最後的解要的:反過來的topo order

  // 檢查同個scc裡面有沒有 p and ~p,如果有,有矛盾,不可能
  rep(i, 0, n) {
    if (scc[i * 2] == scc[i * 2 + 1]) {
      cout << "Impossible\n";
      return 0;
    }
  }
  VI ans;
  rep(i, 0, n) {
    // scc比較小的 出現在拓樸排序的比較後面
    if (scc[i * 2 + 1] < scc[i * 2]) {
      ans.pb(i + 1);  // 這跟題目有關 題目的vertex是1-indexed
    }
  }
  cout << ans.size() << '\n';
  for (int x : ans) {
    cout << x << ' ';
  }
  cout << '\n';
  return 0;
}

標頭、模板請點Submission看 Submission

ojcode 2-SATcodeforceojcodeSCC思維拓樸排序

文章導覽

Previous post
Next post

發佈留言 取消回覆

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

近期文章

  • B. Kyoya and Permutation 解析(思維、數論、DP)
  • G. Guess One Character 解析(思維)
  • E. The Road to Berland is Paved With Good Intentions 解析(思維、2-SAT、SCC、拓樸排序)
  • E. Xor-sequences 解析(思維、矩陣快速冪)
  • LeetCode. First Missing Positive 解析(思維)

近期留言

尚無留言可供顯示。

彙整

  • 2025 年 2 月
  • 2025 年 1 月

分類

  • ojcode
©2026 Logic Formatting | WordPress Theme by SuperbThemes