题意
题解
若出现 p [ i ] [ j ] = 3 p[i][j]=3 p[i][j]=3 的情况,由于没有重边,则至少存在两条 i → j i\rightarrow j i→j 的路径,满足路径上存在非 i , j i,j i,j 的节点;对于这样的两个分别属于不同路径的节点 u , v u,v u,v,它们之间至少存在四条路径,即 u → i → v , u → i → j → v , u → j → v , u → j → i → v u\rightarrow i\rightarrow v,u\rightarrow i\rightarrow j\rightarrow v,u\rightarrow j\rightarrow v,u\rightarrow j\rightarrow i\rightarrow v u→i→v,u→i→j→v,u→j→v,u→j→i→v。于是得到推论:不存在 p [ i ] [ j ] = 3 p[i][j]=3 p[i][j]=3 的情况。
将图划分为连通分量,依次处理。对任一联通分量,两点间路径只可能有 1 , 2 1,2 1,2 两种可能;若存在 p [ i ] [ j ] = 2 p[i][j]=2 p[i][j]=2 的情况,则连通分量中存在环。可以证明,若连通分量中至多存在一个环。反证法,若图中存在两个环,若两个环之间存在割点,则存在两点 i , j i,j i,j,满足 p [ i ] [ j ] = 4 p[i][j]=4 p[i][j]=4;若两个环之间不存在割点,那么存在两个点 i , j i,j i,j,满足 p [ i ] [ j ] = 3 p[i][j]=3 p[i][j]=3。
故对于存在至少一条边的联通分量,只可能为树或者基环树。对于环上节点 u u u,若不存在以其为根的子树,则对任一节点 v v v,满足 p [ u ] [ v ] = 2 p[u][v]=2 p[u][v]=2;若存在以 u u u 为根的子树,设子树接节点构成集合 S S S,那么 S S S 中的任意一对节点 i , j i,j i,j 满足 p [ i ] [ j ] = 1 p[i][j]=1 p[i][j]=1,而 S S S 中的节点 i i i 与不属于 S S S 的节点 j j j 满足 p [ i ] [ j ] = 2 p[i][j]=2 p[i][j]=2。
那么对于任意连通分量,可以如下构造。划分出满足任意两点间路径为 1 1 1 的连通分量,将其任意生成树;对于这样的连通分量,取一点置于环中。满足与所有点间路径为 2 2 2 的节点置于环中。
判断上述两类连通分量的合法性,构造求解即可。总时间复杂度 O ( N 2 ) O(N^2) O(N2)。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l, _ = r; i < _; ++i)
const int maxn = 1005;
typedef vector<int> vec;
typedef vector<vec> mat;
struct CCS
{
int N, nlp, lp[maxn], ntr, tr[maxn];
bool used[maxn];
bool judge(int u, mat &p, mat &b)
{
ntr = 0;
rep(v, 0, N) if (p[u][v] == 1) used[v] = 1, tr[ntr++] = v;
rep(i, 0, ntr)
{
rep(j, 0, ntr) if (p[tr[i]][tr[j]] != 1) return 0;
int cnt = 0;
rep(j, 0, N) cnt += p[tr[i]][j] == 1;
if (cnt != ntr)
return 0;
}
lp[nlp++] = tr[0];
rep(i, 0, ntr - 1)
{
int u = tr[i], v = tr[i + 1];
b[u][v] = b[v][u] = 1;
}
return 1;
}
int construct(mat &p, int cc[], mat &B)
{
N = p.size();
mat b(N, vec(N));
memset(used, 0, sizeof(used));
nlp = 0;
rep(i, 0, N)
{
if (used[i])
continue;
bool hasTree = 0;
rep(j, 0, N) if (i != j && p[i][j] == 1)
{
hasTree = 1;
if (!judge(i, p, b))
return 0;
break;
}
if (!hasTree)
lp[nlp++] = i;
}
bool hasLoop = 0;
rep(i, 0, N) rep(j, i + 1, N) if (p[i][j] == 2)
{
hasLoop = 1;
break;
}
if (hasLoop)
{
if (nlp < 3)
return 0;
rep(i, 0, nlp)
{
int u = lp[i], v = lp[(i + 1) % nlp];
b[u][v] = b[v][u] = 1;
}
}
rep(i, 0, N) rep(j, 0, N) B[cc[i]][cc[j]] = b[i][j];
return 1;
}
} ccs;
int N, ncc, cc[maxn];
bool used[maxn];
void build(mat b);
bool judge(int u, mat &p)
{
ncc = 0;
rep(v, 0, N) if (p[u][v]) used[v] = 1, cc[ncc++] = v;
rep(i, 0, ncc)
{
rep(j, 0, ncc) if (!p[cc[i]][cc[j]]) return 0;
int cnt = 0;
rep(j, 0, N) cnt += (bool)p[cc[i]][j];
if (cnt != ncc)
return 0;
}
return 1;
}
int construct(mat p)
{
N = p.size();
rep(i, 0, N) rep(j, 0, N) if (p[i][j] == 3) return 0;
memset(used, 0, sizeof(used));
mat b(N, vec(N));
rep(i, 0, N)
{
if (used[i])
continue;
bool hasCC = 0;
rep(j, 0, N) if (i != j && p[i][j])
{
hasCC = 1;
if (!judge(i, p))
return 0;
break;
}
if (hasCC)
{
mat _p(ncc, vec(ncc));
rep(j, 0, ncc) rep(k, 0, ncc) _p[j][k] = p[cc[j]][cc[k]];
if (!ccs.construct(_p, cc, b))
return 0;
}
}
build(b);
return 1;
}