题意:给出一棵树,所有边均有向,求从某一点出发,到达其余点经过的逆向边之数(一条逆向边只算一次)的最小值。并给出所有这样的点。
两次dfs的树形DP,第一次针对于子树,第二次针对于整棵树。
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define all(x) (x).begin(), (x).end()
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
typedef long long ll;
typedef pair<int, int> pii;
const int INF =0x3f3f3f3f;
const int maxn= 2*100000 ;
int n;
int fir[maxn+10],nex[2*maxn+20],dp[maxn+10],ans[maxn+10];
struct Edge
{
int to,w;
Edge(){}
Edge(int to,int w):to(to),w(w){}
}edges[2*maxn+10];
int nedge;
inline void add_edge(int x,int y,int w)
{
nex[nedge]=fir[x];
fir[x]=nedge;
edges[nedge++]=Edge(y,w);
}
void init()
{
nedge=0;
memset(fir,-1,(n+1)*sizeof fir[0]);
}
void dfs(int x,int fa)
{
dp[x]=0;
for(int i=fir[x];~i;i=nex[i])
{
Edge& e=edges[i];
int y=e.to;if(y==fa) continue;
int w=e.w;
dfs(y,x);
dp[x]+=w+dp[y];
}
}
void dfs2(int x,int fa,int dis)
{
if(~fa){
ans[x]=ans[fa]+ (dis?-1:+1 ) ;//dp[x]+ans[fa]-dp[x]+(dis?-1:+1);
}
else{
ans[x]=dp[x];
}
for(int i=fir[x];~i;i=nex[i])
{
Edge& e=edges[i];
int y=e.to;if(y==fa) continue;
int w=e.w;
dfs2(y,x,w);
}
}
int main()
{
int x,y;
std::ios::sync_with_stdio(false);
while(cin>>n)
{
init();
for1(i,n-1){
cin>>x>>y;
add_edge(x,y,0);
add_edge(y,x,1);
}
dfs(1,-1);
dfs2(1,-1,0);
/*
for1(i,n)
{
printf("dp[%d]=%d ,ans[%d]=%d\n",i,dp[i],i,ans[i]);
}
putchar('\n');*/
vector<int >ve;
int ret=n-1;
for(int i=1;i<=n;i++) if(ans[i]<=ret)
{
if(ans[i]==ret ){
ve.push_back(i);
}
else {
ret=ans[i];
ve.clear();
ve.push_back(i);
}
}
printf("%d\n",ret);
int siz=ve.size();
for0(i,siz)
{
if(i) putchar(' ');
printf("%d",ve[i]);
}
putchar('\n');
}
return 0;
}