题意
构造满足如下要求的图:
- 图是简单的,并且是连通的
- 总共有\(N\)个点,并且编号为\(1, 2, \dots, N\)
- 有\(M\)条边,并且每条边的长度为\(1\)
- 存在正好\(K\)对节点\((i, j)\),满足最短距离为\(2\)
构造一个满足要求的图,并输出所有的边;如果不存在这样的图,那么输出\(-1\)。
数据范围
\(2 \leq N \leq 100\)
\(0 \leq K \leq \frac{N(N - 1)}{2}\)
思路
从特殊的图的性质入手。首先考虑树。
如果是一棵两层的树,第一层节点数为\(1\),第二层节点数为\(N - 1\)。那么有\(\frac{(N - 1)(N - 2)}{2}\)对节点满足最短路为\(2\)。
如果将第二层的一部分节点变成第二层其他节点的子节点,通过推导可以发现,满足最短距离为\(2\)的点对的个数不多于\(\frac{(N - 1)(N - 2)}{2}\)。
因此,在树中,满足最短距离为\(2\)的点对的个数最多为\(\frac{(N - 1)(N - 2)}{2}\)。
再考虑图的情况,即往树上加边。在两层的树上,每加一条边,满足最短距离为\(2\)的点对的个数减\(1\)。如果第二层\(N - 1\)个点两两加边,那么点对个数为\(0\)。
因此,我们发现,通过对两层树进行加边,满足要求的点对个数在\([0, \frac{(N - 1)(N - 2)}{2}]\)之间连续变化。
我们可以得出做法:如果\(k > \frac{(N - 1)(N - 2)}{2}\),则无解;否则,先建立两层树,然后对\(k - \frac{(N - 1)(N - 2)}{2}\)个点对加边。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> pii;
int n, k;
vector<pii> ans;
int main()
{
scanf("%d%d", &n, &k);
int mx = (n - 1) * (n - 2) / 2;
if(mx < k) {
printf("-1\n");
}
else {
for(int i = 2; i <= n; i ++) {
ans.push_back({1, i});
}
int res = mx - k;
for(int i = 2; i <= n; i ++) {
if(!res) break;
for(int j = i + 1; j <= n; j ++) {
ans.push_back({i, j});
res --;
if(!res) break;
}
}
printf("%d\n", ans.size());
for(auto p : ans) {
printf("%d %d\n", p.first, p.second);
}
}
return 0;
}