F. Traveling
Solution
构造题,注意细节。
考虑怎么样构造用的边数最少,然后如果m更大就加一些不影响任何最短路的边,如果两两连边还是不够m条就输出No。
首先考虑直接从1到n的最短路,显然必须有。
怎么用最少的边来构造呢?首先1到n我们直接连长度为的边,然后考虑的所有点,将这些点按从小到大排序。
顺着考虑这些点,如果,那么我们只需要添加连接1和的边,边权为。
比较麻烦的是如果,考虑添加两条边,分别是连接1和的边、连接和的边,边权如何确定?设两条边权分别为,我们希望走的路径是从1直接走到再直接走到,那么就不能出现从1走到再走到,或者从走到1再走到的情况,为了保证这一点就需要且,也就是我们需要尽量小,因此考虑,如果此时不满足,那么实际上是不可能存在合适的能满足最短路是的,直接判为No,否则就可以连这两条边。
那么是不是对所有这样的点都连两条边呢?实际上考虑第一次连两条边的点是,那么一定满足,因此可以连接和,边权为,这样就只需要连一条边了。
时间复杂度。
vector<pair<int, int> >a;
int d[N];
map<pair<int, int>, bool>mp;
int main() {
int n = fast_IO::read(), m = fast_IO::read();
for (int i = 1; i <= n; ++i) {
d[i] = fast_IO::read();
a.push_back(make_pair(d[i], i));
}
int minn = INF;
for (int i = 1; i <= n; ++i)minn = min(minn, d[i]);
if (d[1] != d[n] || d[1] != minn) {
puts("No");
return 0;
}
sort(a.begin(), a.end());
vector<tuple<int, int, int> >ans;
int last = 0;
for (auto [dist, y] : a) {
if (y == 1)continue;
if (y == n) {
ans.push_back(make_tuple(1, n, d[n]));
mp[make_pair(1, n)] = true;
}
else if ((dist & 1) == (d[n] & 1)) {
ans.push_back(make_tuple(1, y, dist - d[n] >> 1));
mp[make_pair(1, y)] = true;
}
else {
if (!last) {
int a = dist / 2;
int b = (dist + 1) / 2;
if (a + d[n] >= b) {
ans.push_back(make_tuple(1, y, a));
ans.push_back(make_tuple(y, n, b));
mp[make_pair(1, y)] = true;
mp[make_pair(y, n)] = true;
}
else {
puts("No");
return 0;
}
}
else {
if (last < y) {
ans.push_back(make_tuple(last, y, dist - d[last] >> 1));
mp[make_pair(last, y)] = true;
}
else {
ans.push_back(make_tuple(y, last, dist - d[last] >> 1));
mp[make_pair(y, last)] = true;
}
}
last = y;
}
}
if (m < ans.size()) {
puts("No");
return 0;
}
m -= ans.size();
int nowx = 1, nowy = 2;
while (m && nowx <= n) {
if (!mp.count(make_pair(nowx, nowy))) {
ans.push_back(make_tuple(nowx, nowy, 1000000000));
--m;
}
++nowy;
if (nowy > n)++nowx, nowy = nowx + 1;
}
if (m) {
puts("No");
return 0;
}
puts("Yes");
for (auto [x, y, z] : ans)
printf("%d %d %d\n", x, y, z);
return 0;
}