嘘~ 正在从服务器偷取页面 . . .

CF1391E Pairs of Pairs 题解


CF1391E Pairs of Pairs 题解

题目链接:Pairs of Pairs

题意

给出一张无向连通图,选择以下任意一个任务完成:

  • 找到图中一条至少包含 \(\lceil \frac {n} {2} \rceil\) 个点的简单路径。
  • 找到图中偶数(至少 \(\lceil \frac {n} {2} \rceil\) )个点,满足任意两个点对包含的 \(4\) 个点的导出子图至多存在 \(2\) 条边。

若完成任务 \(1\),则输出 PATH,并输出简单路径包含的点数与该路径依次经过的点。

若完成任务 \(2\),则输出 PAIRING,并输出选出的点对数及每一组点对。

可以证明,符合条件的一张图一定可以完成其中一个任务。

输入格式

每个测试包含多个测试用例。

第一行包含测试用例的数量 \(t~(1 \le t \le 10^5)\) 。接下来是每个测试用例的描述。

每个测试用例的第一行包含两个整数 \(n, m~(2 \le n \le 5 \times 10^5,1 \le m \le 10^6)\),分别表示节点数和边数。

接下来的 \(m\) 行,每行包含两个整数 \(u\)\(v~(1 \le u, v \le n,~u \neq v)\),表示给定图中节点 \(u\)\(v\) 之间存在一条无向边。

保证给定的图是连通的且简单的,不包含同一对节点之间的多条边,也不含有自环。

保证所有测试用例中 \(n\) 的总和不超过 \(5 \times 10^5\)。保证所有测试用例中 \(m\) 的总和不超过 \(10^6\)

输出格式

对于每个测试用例,输出格式如下:

如果找到了一种配对,首先输出 PAIRING

  • 然后,输出 \(k~(\lceil \frac{n}{2} \rceil \le 2k \le n)\),表示配对中的对数。

  • 然后,在接下来的 \(k\) 行中,输出两个整数 \(a\)\(b\) ,表示 \(a\)\(b\) 被配对在一起。

    请注意,图中不一定有 \(a\)\(b\) 之间的边!

  • 这种配对必须是有效的,并且每个节点最多只能属于一个配对。

否则,如果找到了一条路径,首先输出 PATH

  • 然后,输出 \(k~(\lceil \frac{n}{2} \rceil \le k \le n)\),表示路径中节点的数量。

  • 然后,在第二行中,按照路径上的顺序输出 \(k\) 个整数 \(v_1, v_2, \ldots, v_k\)

    形式上,对于每个 \(i~(1 \le i < k)\)\(v_i\)\(v_{i+1}\) 应该有一条边连接。

  • 这条路径必须是简单的,意味着每个节点不应重复出现。

原题这翻译不写简单图可还行?

考虑构建原图的 dfs 树,那么只要存在深度大于等于 \(\left\lceil \frac{n}{2} \right\rceil\) 的节点,把它到根的树上路径输出即可。

否则我们将每一层的节点两两配对,如果有奇数个就随便扔掉一个。

下面来证明这样做的正确性。设 \(a,b\)\(c,d\) 分别是两层的某两个节点,且 \(d_a < d_c\)\(d\) 表示深度。

  • \(a,b\) 分别为 \(c,d\) 的父亲,他们的导出子图仅包括两条树边。
  • \(a,b\) 中的某一个是 \(c,d\) 的父亲,同样只有两条树边。
  • \(a,b\)\(c,d\) 没什么特别地树上关系,那最多也只有两条返祖边。

正确性得证。那么这题就做完了。

时间复杂度 \(\mathcal{O}(n+m)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define pb push_back
#define N ((int)(1e6 + 15))

vector<int> vec[N], vec2[N];
int cnt, pos, mxdep, ans1[N], ans2[N], dep[N], fa[N];
void dfs(int u)
{
    dep[u] = dep[fa[u]] + 1; vec2[dep[u]].pb(u);
    if(dep[u] >= mxdep) { mxdep = dep[u], pos = u; }
    for(auto v : vec[u]) if(!dep[v]) { fa[v] = u, dfs(v); }
}
void solve()
{
    int n, m; cin >> n >> m; mxdep = pos = cnt = 0;
    rep(i, 0, n + 5) { dep[i] = fa[i] = 0, vec[i].clear(); vec2[i].clear(); }
    rep(i, 1, m) { int u, v; cin >> u >> v; vec[u].pb(v); vec[v].pb(u); }
    dfs(1);
    for(int i = 1; i <= mxdep; i++)
        for(int j = 0; j + 1 < vec2[i].size(); j += 2) {
            ++cnt; ans1[cnt] = vec2[i][j]; ans2[cnt] = vec2[i][j + 1];
        }
    if((cnt * 2) >= (n + 1) / 2)
    {
        cout << "PAIRING" << '\n' << cnt << '\n';
        rep(i, 1, cnt) cout << ans1[i] << ' ' << ans2[i] << '\n';
    }else
    {
        cout << "PATH" << '\n'; cnt = 0;
        while(pos) { ans1[++cnt] = pos, pos = fa[pos]; }
        cout << cnt << '\n'; rep(i, 1, cnt) cout << ans1[i] << " \n"[i == cnt];
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int qwq; cin >> qwq; while(qwq--) solve();
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/s61kunqm


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录