题目链接:http://codeforces.com/contest/796/problem/D
题目大意:有一棵树,有n个消防点。每个点到最近消防点的距离<=d。题目满足这个条件。问你最多可以拆多少条边,使这个条件仍然满足。
思路:
刚开始我的思路是dfs每个消防点,如果距离>d拆。或者遇到消防点拆。
这个思路的问题在:
如图d=3,那么拆掉红色的,那么1点就不满足了。
正确的思路:因为题目已经满足这个条件。那么我们就相当于把一棵树拆成森林。每个节点属于最近的那个消防点的子树。对每个消防点进行bfs。如果这个点已经访问过,那么说明他距离别的消防点更近,不属于这个消防点。这条边可以拆,注意标记边,访问过的边不能再访问。
#include <bits/stdc++.h>
using namespace std;
struct node{
int v;
int i;
};
vector<node> v[300005];
set<int> s;
bool vis[600005]={0};
int dn[300005]={0};
vector<int> sc;
map<int, int> mp;
queue<int> q;
void bfs(){
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0 ;i<v[u].size(); i++){
node to=v[u][i];
int vv=to.v, ii=to.i;
if(vis[ii]){
continue;
}
vis[ii]=1;
if(dn[vv]){
sc.push_back(ii);
}
else{
dn[vv]=1;
q.push(vv);
}
}
}
return ;
}
int main(){
int n, m, d;
scanf("%d%d%d", &n, &m, &d);
for(int i=1; i<=m; i++){
int x;
scanf("%d", &x);
s.insert(x);
dn[x]=1;
mp[x]=1;
}
for(int i=1; i<=n-1; i++){
int x, y;
scanf("%d%d", &x, &y);
v[x].push_back(node{y, i});
v[y].push_back(node{x, i});
}
for(set<int>::iterator p=s.begin(); p!=s.end(); p++){
q.push(*p);
}
bfs();
printf("%d\n", sc.size());
for(int i=0; i<sc.size(); i++){
printf("%d ", sc[i]);
}
return 0;
}