首页 > 其他分享 >LibreOJ 3818 「CEOI2022」Parking

LibreOJ 3818 「CEOI2022」Parking

时间:2024-05-24 21:08:34浏览次数:9  
标签:sy sx 空栈 LibreOJ int 3818 CEOI2022 maxn bool

考虑把这个停车位当作栈来考虑,每次可以取出栈顶放到另一个栈,并且要保证另一个栈 \(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;
}

标签:sy,sx,空栈,LibreOJ,int,3818,CEOI2022,maxn,bool
From: https://www.cnblogs.com/rizynvu/p/18211662

相关文章

  • loj#575. 「LibreOJ NOI Round #2」不等关系
    记事件\(A\)为「当\(s_i=\texttt<\)时\(p_i<p_{i+1}\)」,事件\(B\)为「当\(s_i=\texttt<\)时\(p_i<p_{i+1}\),且存在\(s_j=\texttt>\),满足\(p_i<p_{i+1}\)。所求即\(n(A)-n(B)\)。\(n(A)\)是好求的,相当于部分定序排列,记每个递增段的长度为\(a_1......
  • loj#566. 「LibreOJ Round #10」yanQval 的生成树
    \(\mu\)取值即所选边权的中位数。把每条边拆成两条(黑边和白边),边权分别为\(\mu-w_i\)和\(w_i-\mu\),要求黑边和白边各选\(\left\lfloor\dfrac{n-1}2\right\rfloor\)条,求最大生成树。可以直接wqs二分,时间复杂度\(\mathcalO(nm\logw~\alpha(n))\)。把所有边的边权......
  • loj#546. 「LibreOJ β Round #7」网格图
    裸的01BFS,时间复杂度\(\mathcalO(nm)\)。相邻的无障碍行可以缩成一行,列同理,所以全图的规模可以缩成\((k+1)\times(k+1)\),再01BFS,时间复杂度\(\mathcalO(k^2)\)。进一步地,所有\(1\timest\)或\(t\times1\)大小的无障碍连通块均可缩成一个点,两个连通块相交,则......
  • loj#523. 「LibreOJ β Round #3」绯色 IOI(悬念)
    由题述,\(X\)满匹配,根据Hall定理,有对于任意一个有\(k\)个妹子的集合,她们能配对的男生的人数\(\gek\)。把每个妹子看作她所连接的两个(可能是同一个)男生间的无向边,则每个连通块必然是树或基环树。问题转化为给每条无向边定向,满足每个点的入度不超过\(1\),求最大边权和。对......
  • LibreOJ-3038 「JOISC 2019 Day3」穿越时空 Bitaro <线段树> 题解
    审题一条链每条边有通行时间上下界限制通过一条边需要\(1\)单位时间站在当前节点时间减少\(1\)耗费\(1\)单位代价\(q\)次询问要么更改一条边的通信时间上下界要么询问在\(b\)时刻在城市\(a\),\(d\)时刻到达城市\(c\)的最小代价思想做题准备......
  • loj#533. 「LibreOJ Round #6」花煎
    非常巧妙的转化。考虑仅计算半边的序列,那么这样的话\(len\)削了一半,要达成的色彩值也开平方了。问题就转化为,将\(l\)拆分为序列\(a\),使得\(\sum_{i=1}^{n}(a_i+1)=l\),且使得\(\prod_{i=1}^{n}a_i\geqk\)的最小\(l\)。经过一些计算,可以发现2的段不超过一个,3的段不......
  • LibreOJ 3591 「USACO 2018.02 Platinum」Cow Gymnasts
    以\(0\)为初始下标。考虑到这个平台之间的转移不是很好处理,于是考虑换个角度,考虑每个高度。这里定义高度为\(i\)的奶牛就是下一次操作要走\(i\)步的奶牛。然后考虑去分析合法序列的性质。性质\(1\):高度为\(x\)的奶牛在移动后的高度依然为\(x\),即这个过程可以看作每......
  • LibreOJ 4114 「联合省选 2024」迷宫守卫
    因为最后的比较方式是字典序,可以明确肯定是贪心的先让第一个数尽量大,然后是第二个数尽量大,以此类推。考虑到如果选出了第一个数,那么按照其\(\text{dfs}\)的方式,相当于是把根到这个树的链删掉了,变成了许多颗子树,然后在按照\(\text{dfs}\)遍历这几个子树的顺序依次在子树中类似......
  • 洛谷-P3380/LibreOJ-106/BZOJ-3196题解
    题意简述给定一个数列,支持以下操作:查询\(k\)在区间内的排名(区间内比\(k\)小的数的个数\(+1\))查询区间内排名为\(k\)的值修改某一位值上的数值查询\(k\)在区间内的前驱(前驱定义为严格小于\(k\),且最大的数,若不存在输出-2147483647)查询\(k\)在区间内的......
  • LibreOJ 3857 「eJOI2017」Experience
    考虑到这一条链肯定是单调递增或者单调递减更优。因为若不是单调的可以考虑把这个链拆成多个单调的链。因为若最大最小值不在链的两端,明显把两端不需要的可以拆出去;否则例如链的顶比底大,则肯定存在\(x>x'<y'>y\),\(x,y\)为链的两端,那么\(x-x'+y-y'\)的收益明显......