「题解 P6830」IOI2020 连接擎天树
这题主要的难点是交互题的形式……调试了快一年
首先我们注意到一个奇怪的数据限制: $p[i][j]\in [0,3]$, 这样的话, 我们可以证明: 对于两个有路径的节点, 他们一定位于一棵树或一棵基环树上
一个联通的无向图可以看作在一棵树的基础上加 $k$ 条边
当 $k=1$ 时, 是一棵基环树
此时, 我们可以发现, 环上各点相互之间都有 $2$ 条互通路径, 而环上连接的树内两两只有 $1$ 条路径
如果我们再加入另一条边 $4-7$:
则一定会形成两个新的环($1-2-3-7-3$ 与 $2-4-7-3-6-5$)
此时, 除了对于原环上的一点 (除了 $2,3$ 两个和 $4,7$ 原来有且只有一条路径的) , 我们能找到至少 $4$ 条到 $4$ 或 $7$ 的路径 (即: 原先通往 $2$ 与 $3$ 的四条路径, 再在 $2$ $3$ 的子树上走到对应的另一棵子树上的 $7$ $4$)
所以可以保证每一个连通块都是树或基环树, 且最大的路径树应为 $2$, 所以出现 $3$ 的情况可以直接排除
对于一个最大路径数为 $1$ 的连通块, 直接建树即可
对于一个最大路径数为 $2$ 的连通块, 根据上面对于基环树的分析, 我们对每个以环上一点为根的子树 ($2-4$ 与 $3-7$) 单独建树, 再在每棵树上各取一点连成环即可
顺便贴一下测试数据与 $check$ 程序领取地址
代码:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 1005;
void build(vector<vector<int> > b);
int n, cnt;
int fa[MAXN], belong[MAXN], vis[MAXN];
vector<int> cir, edg;
vector<vector<int> > gr, tr, ans;
int find(int x) { return fa[x] == x ? fa[x] : fa[x] = find(fa[x]); }
void dfs(int u)
{
vis[u] = 1;
edg.push_back(u);
for (int v = 0; v < n; v++)
if (!vis[v] && gr[u][v] == 1)
dfs(v);
}
int construct(vector<vector<int> > p)
{
gr = p;
n = p[0].size();
tr.clear();
ans.clear();
tr.resize(n + 1);
ans.resize(n);
for (int i = 0; i < n; i++)
{
fa[i] = i;
ans[i].resize(n);
}
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
if (p[i][j] != 0)
{
int fx = find(i), fy = find(j);
if (fx != fy)
fa[fx] = fy;
}
for (int i = 0; i < n; i++)
{
find(i);
if (belong[fa[i]])
belong[i] = belong[fa[i]];
else {
belong[i] = ++cnt;
tr[belong[i]].clear();
belong[fa[i]] = cnt;
}
tr[belong[i]].push_back(i);
}
for (int i = 1; i <= cnt; i++)
{
int siz = tr[i].size();
bool flag = 0;
for (int j = 0; j < siz - 1; j++)
for (int k = j + 1; k < siz; k++)
{
int u = tr[i][j], v = tr[i][k];
if (p[u][v] == 3 || p[u][v] == 0)
return 0;
if (p[u][v] == 2)
flag = 1;
}
if (!flag)
{
for (int j = 0; j < siz - 1; j++)
ans[tr[i][j]][tr[i][j + 1]] = ans[tr[i][j + 1]][tr[i][j]] = 1;
}
else {
cir.clear();
for (int j = 0; j < siz; j++)
{
int u = tr[i][j];
if (vis[u])
continue;
edg.clear();
dfs(u);
int esiz = edg.size();
for (int k = 1; k < esiz; k++)
{
for (int l = 0; l < k; l++)
if (p[edg[k]][edg[l]] != 1)
return 0;
ans[edg[0]][edg[k]] = ans[edg[k]][edg[0]] = 1;
// printf("%d %d\n", edg[0], edg[k]);
}
// printf("%d\n", edg[0]);
cir.push_back(edg[0]);
}
if (cir.size() <= 2)
return 0;
int csiz = cir.size();
for (int j = 0; j < csiz - 1; j++)
ans[cir[j]][cir[j + 1]] = ans[cir[j + 1]][cir[j]] = 1;
ans[cir[0]][cir[csiz - 1]] = ans[cir[csiz - 1]][cir[0]] = 1;
}
}
build(ans);
return 1;
}