ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【题解】CF1284G Seollal

2021-12-16 08:32:57  阅读:148  来源: 互联网

标签:int 题解 拟阵 dsu ++ && mathcal Seollal CF1284G


前置芝士:拟阵交

如果你会,请跳过。

一切证明都略去了……如果需要的话我以后整理一个详细点的吧……

拟阵的定义

我们记一个拟阵(Matroid) \(\mathcal M = \langle S,\mathcal I\rangle\)。

其中,我们称 \(S\) 为 Ground Set,\(\mathcal I\) 为 Family of Sets

请注意此处的 \mathcal I 与后文中的 I 的区别(即 \(\mathcal I\) 与 \(I\) 的区别)。

其中,\(\mathcal I\) 满足 \(\mathcal I \subseteq 2^S\)。

或者你可以这么理解:一个拟阵描述了 \(S\) 中的子集的情况,包含在 \(\mathcal I\) 中即独立,反之不独立。

拟阵的公理

一个拟阵必须满足下面的 \(2\) 个条件:

  1. 对于所有的 \(I \in \mathcal I, I' \subseteq I\),则有 \(I' \in \mathcal I\)。
  2. 若 \(A,B \in \mathcal I, |A| < |B|\),则存在 \(x \in B \setminus A, A + \{x\} \in \mathcal I\)。

图拟阵

\(S\) 是所有的边,\(\mathcal I\) 是所有的生成树。

拟阵交

问题:我们有 \(2\) 个拟阵 \(\mathcal M_1 = \langle S,\mathcal I_1\rangle, \mathcal M_2 = \langle S,\mathcal I_2\rangle\),求 \(I \subseteq S \in \mathcal I_1 \cap \mathcal I_2\),满足 \(I\) 中的所有元素在两个拟阵之中都独立。

算法大概长这样:

  1. \(I = \emptyset\)
  2. 建一个图 \(G_I(I \cup (S \setminus I), E)\),满足 \(\begin{cases}x \in I, y \in (S \setminus I), \langle x, y \rangle \in E \Leftrightarrow I - x + y \in \mathcal{I}_1 \\ x \in I, y \in (S \setminus I), \langle x, y \rangle \in E \Leftrightarrow I - x + y \in \mathcal{I}_2 \end{cases}\)。
  3. 寻找图中的从 \(y_1\) 到 \(y_{k+1}\) 的最短路,满足 \(I+y_1 \in \mathcal I_1\),\(I+y_2\in \mathcal I_2\)。记 \(x\) 到 \(y\) 的最短路径为 \(\operatorname{path}(x,y)\)。
  4. 把 \(I\) 换成 \(I \oplus \{y_1, x_1, \dots, x_k, y_{k + 1}\}\).

时间复杂度为 \(\mathcal O(|S|^3)\)。

Analysis

题目中所说的“只有一条简单路径”,即我们需要构造一棵生成树,且要满足叶节点的颜色一定是白色。

我们注意到黑色的节点只与白色节点相邻,所以黑色的点集和白色的点集是 \(2\) 个独立集。

下面有一个和显然今天下大雨一样显然的结论。

能找到一个无环的子图使得所有的黑点的度都为 \(2\) 是答案存在的充必条件。

由于它是无环子图,则我们只需无脑添上一些边就可以使得他变成一颗生成树。而树的叶子节点的度数为 \(1\),所以添边后形成的生成树的符合条件的。

如果存在答案,我们将生成树中的一些边删去,则我们一定可以得到一个无环子图使得黑点的度都是 \(2\)。\(\blacksquare\)

接下来我们考虑怎么处理这个问题。

我们先从“无环”入手。

考虑图拟阵的定义:\(\mathcal M_1 = \langle S,\mathcal I_1\rangle\),其中 \(S\) 为所有边的集合,\(\mathcal I_1\) 为所有的生成树的集合。我们所要求的无环子图是我们所要求的生成树里面的一部分,所以这个无环子图在图拟阵 \(\mathcal M_1\) 里一定是独立的,而有环的子图一定是不独立的。

但是,度数这个条件怎么满足?

我们发现度数恰好为 \(2\) 是很难构造的,但是如果我们有度数 \(\le 2\),判断是否符合要求是很简单的。

我们考虑构造一个拟阵 \(\mathcal M_2 = \langle S,\mathcal I_2\rangle\),其中 \(S\) 仍然为所有边的集合(废话,否则咋拟阵交),而 \(\mathcal I_2\) 需要满足所有黑点度数 \(\le 2\) 的方案都独立。

然后我们算一下 \(\mathcal M_1\) 和 \(\mathcal M_2\) 的交就可以啦!

边数为 \(\mathcal O(nm)\),所以总的时间复杂度为 \(\mathcal O(n^3m^3)\)。

啊哈,是不是蒯个拟阵交的板子就好了呢~

然而不是的,因为细节贼多,贼容易写挂……

核心Code(void io::pc(char) 是输出,dsu是封装好的并查集):

class Matroid
{
private:
  const int fx[4] = {-1, 0, 0, 1};
  const int fy[4] = {0, -1, 1, 0};

  struct tuple
  {
    int x, y;
  };

  int n0, n1, n, m, demands;
  int count[801], dist[801], prev[801], que[801];
  bool vis[805], ok1[801], ok2[801];
  char grid[21][21], ans[41][41];
  tuple E[801];

#define make_tuple(x, y) ((tuple){x, y})
#define id(x, y) ((x)*n1 + y)
#define valid(x, y) ((x + y) > 0 && (x + y) % 2 == 0)
#define checkx(x) (x >= 0 && x < n0)
#define checky(x) (x >= 0 && x < n1)

  IL void get_edges(int i, int j)
  {
    for (int k = 0; k < 4; ++k)
    {
      int u = i + fx[k], v = j + fy[k];
      if (checkx(u) && checky(v) && grid[u][v] == 'O')
        E[m++] = make_tuple(id(i, j), id(u, v));
    }
  }

  bool augument()
  {
    dsu.init(n);
    memset(count, 0, sizeof(count));
    memset(dist, -1, sizeof(dist));
    for (int i = 0; i < m; ++i)
      if (vis[i])
      {
        dsu.merge(E[i].x, E[i].y);
        ++count[E[i].x];
      }
    int tail = 0;
    for (int i = 0; i < m; ++i)
    {
      ok1[i] = !vis[i] && dsu.find(E[i].x) != dsu.find(E[i].y);
      ok2[i] = !vis[i] && count[E[i].x] < 2;
      if (ok1[i])
      {
        dist[i] = 0, prev[i] = -1, que[tail++] = i;
        if (ok2[i])
        {
          vis[i] = true;
          return true;
        }
      }
    }
    for (int head = 0; head < tail; ++head)
    {
      int u = que[head];
      if (ok2[u])
      {
        while (~u)
          vis[u] ^= 1, u = prev[u];
        return true;
      }
      if (vis[u])
      {
        dsu.init(n);
        for (int i = 0; i < m; ++i)
          if (vis[i] && i != u)
            dsu.merge(E[i].x, E[i].y);
        for (int v = 0; v < m; ++v)
          if (!vis[v] && dsu.find(E[v].x) != dsu.find(E[v].y) && dist[v] == -1)
            dist[v] = dist[u] + 1, prev[v] = u, que[tail++] = v;
      }
      else
        for (int v = 0; v < m; ++v)
          if (vis[v] && count[E[u].x] - (E[u].x == E[v].x) < 2 && dist[v] == -1)
            dist[v] = dist[u] + 1, prev[v] = u, que[tail++] = v;
    }
    return false;
  }

public:
  void solve()
  {
    n0 = io::in(), n1 = io::in();
    for (int i = 0; i < n0; ++i)
      io::gets(grid[i]);
    n = n0 * n1, m = demands = 0;
    for (int i = 0; i < n0; ++i)
      for (int j = 0; j < n1; ++j)
        if (grid[i][j] == 'O' && valid(i, j))
          demands += 2, get_edges(i, j);
    while (augument())
      --demands;
    if (demands)
      pc('N'), pc('O'), pc('\n');
    else
    {
      pc('Y'), pc('E'), pc('S'), pc('\n');
      get_edges(0, 0);
      dsu.init(n);
      for (int t = 0; t < 2; ++t)
        for (int i = 0; i < m; ++i)
        {
          bool connected = dsu.find(E[i].x) == dsu.find(E[i].y);
          if (t && !connected)
            vis[i] = true;
          if (vis[i] && !connected)
            dsu.merge(E[i].x, E[i].y);
        }
      for (int i = 0; i < n0 * 2 - 1; ++i)
        for (int j = 0; j < n1 * 2 - 1; ++j)
          ans[i][j] = ' ';
      for (int i = 0; i < n0; ++i)
        for (int j = 0; j < n1; ++j)
          ans[i * 2][j * 2] = grid[i][j];
      for (int i = 0; i < m; ++i)
        if (vis[i])
        {
          int s = min(E[i].x, E[i].y);
          int s1 = max(E[i].x, E[i].y);
          if (s + 1 == s1)
            ans[s / n1 * 2][s % n1 * 2 + 1] = 'i';
          else
            ans[s / n1 * 2 + 1][s % n1 * 2] = '!';
        }
      for (int i = 0; i < n0 * 2 - 1; ++i)
      {
        for (int j = 0; j < n1 * 2 - 1; ++j)
          pc(ans[i][j]);
        pc('\n');
      }
    }
  }
};

标签:int,题解,拟阵,dsu,++,&&,mathcal,Seollal,CF1284G
来源: https://www.cnblogs.com/zght/p/15696233.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有