My Solution
题意:有一棵无根树,有一些节点上有一些标记(police station),初始时满足,每个节点至少 与一个被标记过的节点相连且距离不超过d,要求去掉尽可能多的节点,使剩余的图依然满足,每个节点至少 与一个被标记过的节点相连且距离不超过d。
最短路、BFS
本来用dfs写了一发,后来发现很多情况下会重复去掉边。
用BFS来做比较好,从所有的被标记过的节点开始BFS,当接下来的边会导向已经访问过的节点时把这条边减去,否则放入队列。
这样每个节点都会与一个被标记过的节点直接或间接的相连,且距离不超过d。
最终会被减掉m-1条边,m表示size{unique{pi}}.
这里可以用反证法证明,如果减掉m-1+a(a>0)条边,则图会被切成大于等于m+1块,然后只有m个点被标记,所以必定有些块不满足要求。
反之,如果减点 m-1-a(a>0),则图会边切成小于等于m-1块,这个时候根据鸽巢原理,至少存在一个块中有2个节点被标记,显然还有更优的方案。
复杂度 O(n)
- #include
- #include
- #include
- #include
- #include
- #include
- using namespace std;
- typedef long long LL;
- typedef pair<int, int> ii;
- const int MAXN = 3e5 + 8;
- vector sons[MAXN];
- queue que;
- bool vis[MAXN], f[MAXN];
- inline void bfs()
- {
- int fa, u, v, sz, i;
- while(!que.empty()){
- u = que.front().first;
- fa = que.front().second;
- que.pop();
- if(vis[u]) continue;
- vis[u] = true;
- sz = sons[u].size();
- for(i = 0; i < sz; i++){
- v = sons[u][i].first;
- if(v == fa) continue;
- if(vis[v]){
- f[sons[u][i].second] = true;
- }
- else que.push(ii(v, u));
- }
- }
- }
- int main()
- {
- #ifdef LOCAL
- freopen("d.txt", "r", stdin);
- //freopen("d.out", "w", stdout);
- int T = 4;
- while(T--){
- #endif // LOCAL
- ios::sync_with_stdio(false); cin.tie(0);
- int n, k, d, i, u, v, ans = 0;
- cin >> n >> k >> d;
- for(i = 1; i <= k; i++){
- cin >> u;
- que.push(ii(u, -1));
- }
- for(i = 1; i < n; i++){
- cin >> u >> v;
- sons[u].push_back(ii(v, i));
- sons[v].push_back(ii(u, i));
- }
- bfs();
- for(i = 1; i <= n; i++){
- if(f[i]) ans++;
- }
- cout << ans << "n";
- ans = 0;
- for(i = 1; i <= n; i++){
- if(f[i]){
- if(ans) cout << " ";
- ans++;
- cout << i;
- }
- }
- #ifdef LOCAL
- for(i = 1; i <= n; i++) sons[i].clear();
- memset(vis, false, sizeof vis);
- memset(f, false, sizeof f);
- cout << endl;
- }
- #endif // LOCAL
- return 0;
- }
非特殊说明,本博所有文章均为博主原创,未经许可不得转载。
https://www.prolightsfxjh.com/
Thank you!
------from ProLightsfx
非特殊说明,本博所有文章均为博主原创,未经许可不得转载。
如经许可后转载,请注明出处:https://prolightsfxjh.com/article/codeforces-round-408-div-2-d-police-stations/
共有 0 条评论