LibreOJ 3818 「CEOI2022」Parking

rizynvu發表於2024-05-24

考慮把這個停車位當作棧來考慮,每次可以取出棧頂放到另一個棧,並且要保證另一個棧 \(sz\le 2\),且當 \(sz = 2\) 時要保證棧頂棧底都是同一種元素。

\((x, y)\) 表示 \(x\) 為棧頂 \(y\) 為棧底,\((0, x)\) 表示棧中元素只有 \(x\)

考慮對於 \((x, y)\),連一條 \(x\to y\) 的邊,若棧中元素數量 \(= 1\) 就不連。

那麼對於連邊出來的一條鏈,相當於是 \((0, x), (x, y), (y, z)\) 這樣的,那麼可以透過 \(len - 1\) 次操作變為 \((x, x), (y, y), (0, z)\)
同時對於 \((0, p), (0, p)\),可以合併出 \((p, p), (0, 0)\),就多了一個空棧。

因為這種操作是不需要空棧的,所以可以先把這些操作做了。

還有可能是個環的樣子,那麼相當於是 \((x, y), (y, z), (z, x)\)
那麼這個可以用一個空棧,先變為 \((0, y), (y, z), (z, x), (0, x)\),再變為 \((y, y), (z, z), (0, x), (0, x)\),再合併一下變為 \((y, y), (z, z), (x, x), (0, 0)\)
\(sz + 1\) 次操作即可做完。

同時考慮剩下的只可能是 \((0, x)\) 或者 \((x, y), (x, z), (y, p), (z, q)\) 之類的。
首先對於前面的不考慮。
對於後面的,只需要一個空棧放入 \((x, x)\),剩下就變成兩條鏈 \((0, y), (y, p)\)\((0, z), (z, q)\)
那麼就可以把鏈做完了。

但是能發現若對於 \(p, q\),存在 \((0, p)\),那麼在拆完後就還能得到一個 \((p, p), (0, 0)\),返回一個空棧。
於是可以按照一個優先順序來選,即拆完後能得到的空棧越多優先順序就越高。

當然也可以考慮對於葉子節點 \(p, q\) 連邊。
那麼只會有鏈和環的情況。
其中鏈是每一步都能還一個空棧,最後一步能還兩個;環是第一步不還,最後一步還兩個,其他還一個。
所以能發現先刪鏈再刪環是更優的。

能發現對於剛剛的構造方式,如果能不移是肯定不會移的,所以操作次數肯定是最少的。

時間複雜度 \(\mathcal{O}(n)\)

#include<bits/stdc++.h>
const int maxn = 2e5 + 10;
int n, m;
int sx[maxn], sy[maxn];
std::vector<std::pair<int, int> > ans;
inline void Solve(int x, int y) {
   ans.emplace_back(x, y);
   assert(sy[x]), assert(! sx[y]);
   int &s = sx[x] ? sx[x] : sy[x], &t = sy[y] ? sx[y] : sy[y];
   t = s, s = 0;
   assert(! sx[y] || sx[y] == sy[y]);
}
namespace Solve1 {
   std::vector<int> to[maxn];
   int in[maxn], w[maxn];
   inline void Main() {
      for (int i = 1; i <= n; i++)
         to[i].clear(), in[i] = 0, w[i] = 0;
      for (int i = 1; i <= m; i++) {
         if (sx[i] && sx[i] != sy[i])
            to[sx[i]].push_back(i), in[sy[i]]++;
         if (! sx[i] && sy[i])
            w[sy[i]] = i;
      }
      for (int i = 1; i <= n; i++)
         if (! in[i] && to[i].size() == 1) {
            int l = w[i];
            for (int u = i; ; u = sy[to[u][0]]) {
               if (to[u].empty()) break;
               Solve(to[u][0], l), l = to[u][0];
            }
         }
      for (int i = 1; i <= n; i++)
         w[i] = 0;
      for (int i = 1; i <= m; i++) {
         if (sx[i] || ! sy[i]) continue;
         if (w[sy[i]]) Solve(i, w[sy[i]]);
         else w[sy[i]] = i;
      }
   }
}
namespace Solve2 {
   std::vector<int> to[maxn];
   bool vis[maxn];
   inline bool Main() {
      for (int i = 1; i <= n; i++)
         to[i].clear(), vis[i] = 0;
      for (int i = 1; i <= m; i++)
         if (sx[i] && sx[i] != sy[i])
            to[sx[i]].push_back(i);
      int e = 0;
      for (int i = 1; i <= m; i++)
         if (! sx[i] && ! sy[i])
            e = i;
      for (int s = 1; s <= n; s++) {
         if (vis[s]) continue;
         std::vector<int> P;
         bool f = 1; int u = s;
         do {
            if (vis[u]) {f = 0; break;}
            vis[u] = 1;
            if (to[u].size() != 1) {f = 0; break;}
            P.push_back(to[u][0]), u = sy[to[u][0]];
         } while (u != s);
         if (! f) continue;
         if (! e) return false;
         Solve(P[0], e);
         for (int i = 0; i + 1 < P.size(); i++)
            Solve(P[i + 1], P[i]);
         Solve(e, P.back());
      }
      return true;
   }
}
namespace Solve3 {
   std::vector<int> to[maxn], G[maxn];
   int tx[maxn], ty[maxn], w[maxn];
   inline bool Main() {
      for (int i = 1; i <= n; i++)
         to[i].clear(), G[i].clear(), w[i] = 0;
      std::vector<int> e;
      for (int i = 1; i <= m; i++) {
         if (sx[i] && sx[i] != sy[i])
            to[sx[i]].push_back(i);
         if (! sx[i] && sy[i])
            w[sy[i]] = i;
         if (! sx[i] && ! sy[i])
            e.push_back(i);
      }
      for (int i = 1; i <= n; i++)
         if (to[i].size() == 2) {
            tx[i] = sy[to[i][0]], ty[i] = sy[to[i][1]];
            while (to[tx[i]].size()) tx[i] = sy[to[tx[i]][0]];
            while (to[ty[i]].size()) ty[i] = sy[to[ty[i]][0]];
            G[tx[i]].push_back(i), G[ty[i]].push_back(i);
         }
      auto DO = [&](int s) {
         for (int l = s; ; ) {
            int i = G[l][0];
            int idx = to[i][0], idy = to[i][1];
            if (e.empty())
               return false;
            int we = e.back(); e.pop_back();
            Solve(idx, we), Solve(idy, we);
            while (to[sy[idx]].size())
               Solve(to[sy[idx]][0], idx), idx = to[sy[idx]][0];
            while (to[sy[idy]].size())
               Solve(to[sy[idy]][0], idy), idy = to[sy[idy]][0];
            if (w[tx[i]]) Solve(idx, w[tx[i]]), e.push_back(idx);
            else w[tx[i]] = idx;
            if (w[ty[i]]) Solve(idy, w[ty[i]]), e.push_back(idy);
            else w[ty[i]] = idy;
            G[tx[i]].erase(std::find(G[tx[i]].begin(), G[tx[i]].end(), i));
            G[ty[i]].erase(std::find(G[ty[i]].begin(), G[ty[i]].end(), i));
            if (G[tx[i]].empty() && G[ty[i]].empty())
               break;
            l = G[tx[i]].empty() ? ty[i] : tx[i]; 
         }
         return true;
      };
      for (int s = 1; s <= n; s++)
         if (G[s].size() == 1) 
            if (! DO(s)) return false;
      for (int s = 1; s <= n; s++)
         if (G[s].size() == 2)
            if (! DO(s)) return false;
      return true;
   }
}
int main() {
   scanf("%d%d", &n, &m);
   for (int i = 1, k; i <= m; i++) scanf("%d%d", &sy[i], &sx[i]);
   Solve1::Main();
   if (! Solve2::Main())
      return puts("-1"), 0;
   if (! Solve3::Main())
      return puts("-1"), 0;
   for (int i = 1; i <= n; i++)
      assert(sx[i] == sy[i]);
   printf("%zu\n", ans.size());
   for (auto _ : ans)
      printf("%d %d\n", _.first, _.second);
   return 0;
}