题目
题意:
给定一棵树,树上的节点有k个点是警察局。要求拆掉树上尽可能多的边,但要保证每个点离警察局的点的距离不超过d。
分析:
有很多满足条件的点其实就应该想到超级原点,有了超级原点的想法后这题的思路就很自然了。建立一个超级原点连向警察局的那些点,那么就是建一棵树(因为要删去尽可能多的边),使得树上的点到原点的距离都小于等于k+1。那么以原点开始bfs,对于一个点要更新它原来相连的点时,如果那个点已经被更新了,那么说明那个点可以通过别的边到达原点,而不需要当前这条边,这种情况下就可以删去这条边了。
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <queue>
using namespace std;
map<int,map<int,int> >id;
vector<int> p,g[300005];
int vis[300005],flag[300005],pre[300005];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,k,d;
cin >> n >> k >> d;
for (int i = 1; i <= k; i++)
{
int x;
cin >> x;
if( flag[x] == 0 )
{
p.push_back(x);
flag[x] = 1;
}
}
for (int i = 1; i < n; i++)
{
int x,y;
cin >> x >> y;
id[x][y] = id[y][x] = i;
g[x].push_back(y);
g[y].push_back(x);
}
set<int> ans;
queue<int> q;
for (int i = 0; i < p.size(); i++)
{
q.push(p[i]);
vis[p[i]] = 1;
}
while( !q.empty() )
{
int x = q.front();
q.pop();
for (int j = 0; j < g[x].size(); j++)
{
int t = g[x][j];
if( vis[t] != 0 && pre[x] != t )
{
ans.insert(id[x][t]);
continue;
}
if( vis[t] ) continue;
pre[t] = x;
vis[t] = 1;
q.push(t);
}
}
cout << ans.size() << '\n';
set<int>:: iterator it;
for (it = ans.begin(); it != ans.end(); it++)
{
cout << *it;
if( it == --ans.end() ) cout << '\n';
else cout << ' ';
}
return 0;
}