QOJ 3223 Cellular Automaton

rizynvu發表於2024-05-30

\(k\) 為題面中的 \(w\)

考慮將 \(S'\) 右平移 \(k\) 位,那麼就可以看作是 \(S'_i = P(S_{i - 2k\sim i})\)

考慮對於 \(S = \cdots\texttt{00110011}\cdots\),對於 \(S'\),那麼對應 \(\texttt{0011}\)\(\texttt{1}\) 的數量也應該是 \(2\)
那麼否則只需要多複製幾遍,\(S\)\(S'\) \(\texttt{1}\) 的個數肯定不會 \(= 0\)

考慮刻畫出來這個 \(\texttt{1}\) 個數相同的限制。
考慮令狀態為一個長度為 \(2k\) 的二進位制 \(u\),接下來考慮在後面加一個 \(w = 0 / 1\) 得到 \(s = 2u + w\),同時就令 \(v\)\(s\) 拋棄最高位的 \(2k\) 位二進位制數。
那麼連邊 \((u, v, w - P(s))\),能發現對於這組 \(P\) 合法的條件就是對於這個圖中的任意一個環,權值和 \(= 0\)
否則考慮找到一個權值和 \(\not = 0\) 的環,找到對應的變化狀態,並複製很多份,這樣的 \(S\) 肯定無解。

但是考慮到環權值可能有 \(< 0\) 也可能是 \(> 0\) 的。
於是考慮引入反向邊,對於邊 \((u, v, w)\),再加入邊 \((v, u, -w)\)
那麼這樣對於權值 \(> 0\) 的環,透過走反向邊,就能走出權值 \(< 0\) 的環了。
於是就只需要判負環了,如果有負環就說明無解。

如果直接暴力列舉 \(P\) 然後跑 SPFA,複雜度 \(\mathcal{O}(2^{2^{2k + 1}}\times \operatorname{SPFA}(2^{2k}, 2^{2k + 1}))\),很沒有前途。

但是考慮到有個 \(Q\) 的限制,比較直接的做法就是先列舉最長的 \(\operatorname{LCP}(P, Q)\),再一步一步向後擴充套件。
那麼接下來就涉及到一個問題,就是已知 \(P\) 的部分,有一部分為止,如何判斷是否有解。
首先對於 \((u, v, w - P(s))\),如果 \(s\) 已知那還是一樣連邊。
否則考慮對於 \(w = 0 / 1\) 討論一下。

考慮 \(w = 0\) 的情況。
那麼因為 \(P(s) = 0 / 1\),邊權肯定為 \(0 / -1\)
那麼刻畫到最短路上就相當於是 \(dis_u \ge dis_v, dis_v\ge dis_u\)\(dis_u - 1\ge dis_v, dis_v + 1\ge dis_u\)
考慮到 \(dis_u\ge dis_v, dis_v + 1\ge dis_u\) 等價於這個條件。
對於必要性,因為選取的是更寬鬆的情況,顯然。
對於充分行,考慮到此時合法的只會有 \(dis_u = dis_v, dis_u - 1 = dis_v\) 兩種情況,這兩種情況正好分別對應著邊權 \(= 0, -1\) 的情況。
於是可以連邊 \((u, v, 0), (v, u, 1)\)
對於 \(w = 1\),證明類似,可以連邊 \((u, v, 1), (v, u, 0)\)

這相當於是個差分約束的形式,那麼如果存在負環就說明無解。

於是複雜度最佳化到了 \(\mathcal{O}(2^{2k + 1}\times \operatorname{SPFA}(2^{2k}, 2^{2k + 1}))\)

還能繼續最佳化。
考慮到因為在圖中加入了反向邊,且這個圖的限制較緊:對於 \(P(s)\) 已知的,相當於直接給定了 \(dis_u, dis_v\) 的等式的關係,否則 \(dis_u, dis_v\) 也只會有至多 \(1\) 的偏差。
那麼如果一個負環被鬆弛了過後,那麼整個圖應該都會隨著一起被鬆弛。
因為 \(|dis_s|\le 2k\),所以 \(\sum\limits_{s = 0}^{2^{2k} - 1} |dis_s|\le 2k\times 2^{2k}\),所以在不超過 \(2k\times 2^{2k}\) 次鬆弛後整個圖所有的 \(dis\) 都會 \(< 0\)

於是令 \(dis_0 = 0\) 為起點,終止條件改為無鬆弛或 \(dis_0 < 0\),就能在 \(\mathcal{O}(2k\times 2^{2k})\) 的複雜度跑出。
\(dis_0 < 0\),則說明 \(0\) 被負環鬆弛到了,那麼就說明存在負環,無解。

時間複雜度 \(\mathcal{O}(2^{2k + 1}\times 2k\times 2^{2k})\)

#include<bits/stdc++.h>
const int maxn = 1 << 7;
int dis[maxn], vis[maxn];
std::vector<std::pair<int, int> > to[maxn];
int S[maxn];
int main() {
   int w, m, n;
   scanf("%d", &w), m = 1 << w * 2, n = m << 1;
   for (int i = 0; i < n; i++) scanf("%1d", &S[i]);
   auto check = [&](int lim) {
      for (int u = 0; u < m; u++)
         to[u].clear(), dis[u] = 1e9, vis[u] = 0;
      for (int u = 0; u < m; u++)
         for (int w : {0, 1}) {
            int p = (u << 1) | w, v = p & (m - 1);
            if (p <= lim)
               to[u].emplace_back(v, w - S[p]), to[v].emplace_back(u, S[p] - w);
            else if (! w)
               to[u].emplace_back(v, 0), to[v].emplace_back(u, 1);
            else
               to[u].emplace_back(v, 1), to[v].emplace_back(u, 0);
         }
      std::queue<int> Q;
      dis[0] = 0, Q.push(0);
      while (! Q.empty() && dis[0] >= 0) {
         int u = Q.front();
         Q.pop(), vis[u] = 0;
         for (auto _ : to[u]) {
            int v = _.first, w = _.second;
            if (dis[u] + w < dis[v])
               dis[v] = dis[u] + w, ! vis[v] && (Q.push(v), vis[v] = 1);
         }
      }
      return dis[0] >= 0;
   };
   if (check(n - 1)) {
      for (int i = 0; i < n; i++) printf("%d", S[i]);
      return puts(""), 0;
   }
   for (int i = n - 1; ~ i; i--)
      if ((S[i] ^= 1) && check(i)) {
         for (int j = i + 1; j < n; j++)
            S[j] = 0, S[j] ^= ! check(j);
         for (int j = 0; j < n; j++) printf("%d", S[j]);
         return puts(""), 0;
      }
   puts("no");
   return 0;
}