题解:P3505 [POI2010] TEL-Teleportation
posted on 2024-12-19 09:12:33 | under | source
小清新图论题。
题意等价于加尽量多边使得 \(1\to 2\) 最短路边长 \(\ge 5\)。边权都是 \(1\),所以不难想到 bfs,那么条件等价于从 \(1\) 开始跑 bfs,然后按 bfs 的过程分层,\(1\) 在第一层,那么 \(2\) 的层数需满足 \(\ge 6\)。
bfs 分层的限制是:每一层的点只能被上一层的点连边,且必须被至少一个上一层的点连边。还是不好做,于是放宽限制,不要求与至少一个上一层的点连边,但是不能有空层。可以发现,不会影响判定,因为这样搞每个点的层数只会比原先小,非法情况依旧非法。
同时,并不在意除 \(2\) 之外层数 \(\ge 6\) 的点到底在哪,所以不妨将它们都扔到第 \(5\) 层,显然无影响。也就是说,我们需要确定一个分层方案,满足第一层只有 \(1\)、第六层只有 \(2\)、一个点只能向它相邻层的点连边(包括自己层),且每层至少有一个点。
再考虑原图的边 \((u,v)\) 对分层的限制,即 \(u,v\) 必须处于相邻层或同一层。
那么问题可以转述为:依次考虑每个点,假如放在第 \(k\) 层,那么需原图与之相邻的点的层数 \(p\) 只能是 \(k-1,k,k+1\),然后会产生 \(k-1,k,k+1\) 层的点数的贡献,要令总贡献最大。
明确一点,放 \(3\) 层肯定比放 \(2\) 层“发展潜力”大,也就是会对之后的点贡献更多,因为后者还可对 \(4\) 层贡献。
首先与 \(1\) 相邻的点只能放 \(2\) 层,与 \(2\) 相邻同理。再考虑与这些点相邻的点,若是与 \(1\) 相邻的点相邻,则可以放 \(2,3\) 层,那我一定放 \(3\) 层。为啥?考虑调整法,最终局面每层必有点,然后将这个点从 \(2\) 调整至 \(3\) 层一定不劣。其它情况同理。
再考虑剩下的 \(cnt\) 个点。容易发现放 \(3\) 层一定优于 \(2\) 层,因为 \(4\) 层至少存在一个点;同理 \(4\) 层一定优于 \(5\) 层。而且还不用管这些点在原图的连边情况,因为分层方案中它们一定相邻。那么放 \(3\) 还是 \(4\) 呢?
注意到剩下的点互相连边的边数是确定的,为 \(\frac {cnt(cnt-1)}2\)。所以只需要令它们与原先的点连边数最多。记原先 \(2,3,4,5\) 层的点数分别为 \(a,b,c,d\),那么贡献即为 \(cnt\max(a+b+c,b+c+d)\)。
复杂度 \(O(n+m)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5 + 5;
int n, m, u, v, a, b, c, d;
int col[N];
LL ans, cnt;
vector<int> to[N];
signed main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i) scanf("%d%d", &u, &v), to[u].push_back(v), to[v].push_back(u);
col[1] = 1, col[2] = 6;
for(auto i : to[1]) col[i] = 2, ans += a + 1, ++a;
for(auto i : to[2]) col[i] = 5, ans += d + 1, ++d;
for(int i = 3; i <= n; ++i)
if(col[i] == 2){
for(auto j : to[i])
if(!col[j]) col[j] = 3, ans += a + b + c, ++b;
}
else if(col[i] == 5){
for(auto j : to[i])
if(!col[j]) col[j] = 4, ans += b + c + d, ++c;
}
for(int i = 3; i <= n; ++i) cnt += (col[i] == 0);
ans += 1ll * cnt * (cnt - 1) / 2;
ans += 1ll * max(a + b + c, b + c + d) * cnt;
cout << ans - m;
return 0;
}

浙公网安备 33010602011771号