题意:
给出一颗有方向的n个节点的树,现在要选择一个点作为首都。
问最少需要翻转多少条边,使得首都可以到所有其他的城市去,以及相应的首都可以是哪些点。
思路:
- (暴力) 对每个点用dfs求一次需要翻转的道路条数,显然,在n较小的情况下可行,但n<=2e5,会超时。
- (换根求值)观察手模发现,如果我们先假设1为根,并求出此时的答案ans,对于求下一个点i,我们可以发现一些规律,我们只需要修改原来以1为根的树的从1到i这条路径上的边即可,假设以1为根的树求值时在1到i要修改的边数为cnt 则答案为(i到1路径边数)- cnt + ans - cnt (1-i以外的修改不变) 所以我们只需呀在秋1的时候预处理每个点到根路径的边数和这条边上需要修改的边数即可。
-
(树形dp)dp[i]表示i节点到它的子树所有点最少需要翻转的边。先预处理根节点作为首都需要翻转的边,再精选dp dp[i]表示i作为首都需要翻转的最少边的数量 如果u-v的方向是u-->v,那么dp[v]=dp[u]+1;,否则dp[v]=dp[u]-1。
换根公式:
#include<bits/stdc++.h>
#define PII pair<int,int>
#define ls u<<1
#define rs u<<1|1
#define xx first
#define yy second
#define rd(x) scanf("%lld",&x)
#define wt(x) printf("%lld\n",x)
#define lowbit(i) i&-i
#define int long long
using namespace std;
const int N = 2e5+2;
const int mod = 998244353;
int tot,ne[N<<1],head[N<<1],to[N<<1],w[N<<1];
void add(int u,int v,int x)
{
to[++tot] =v;
w[tot] = x;
ne[tot] = head[u];
head[u] = tot;
}
int dep[N],cnt[N],ans;
void dfs1(int x,int f)
{
for(int i=head[x];i;i=ne[i])
{
int v = to[i] ;
if(v==f) continue;
cnt[v] = cnt[x] + (w[i]==0);
ans += (w[i]==0);
dep[v] = dep[x] + 1;
dfs1(v,x);
}
}
void sol()
{
int n;
cin>>n;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
add(u,v,1);
add(v,u,0);
}
ans=0;
dfs1(1,0);
int tot_num = ans;
vector<int> res;
res.push_back(1);
for(int i=2;i<=n;i++)
{
int new_num = tot_num + dep[i] - 2*cnt[i];
if(new_num > ans) continue;
else if(new_num == ans) res.push_back(i);
else
{
ans = new_num;
while(res.size()) res.pop_back();
res.push_back(i);
}
}
cout<<ans<<endl;
for(int i=0;i<(int)res.size();i++) cout<<res[i]<<' ';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int t;
t=1;
// cin>>t;
while(t--)
sol();
return 0;
}
树形dp:
#include<bits/stdc++.h>
#define PII pair<int,int>
#define ls u<<1
#define rs u<<1|1
#define xx first
#define yy second
#define rd(x) scanf("%lld",&x)
#define wt(x) printf("%lld\n",x)
#define lowbit(i) i&-i
#define int long long
using namespace std;
const int N = 2e5+2;
const int mod = 998244353;
int tot,ne[N<<1],head[N<<1],to[N<<1],w[N<<1];
void add(int u,int v,int x)
{
to[++tot] =v;
w[tot] = x;
ne[tot] = head[u];
head[u] = tot;
}
int dp[N];
void dfs1(int x,int f)
{
for(int i=head[x];i;i=ne[i])
{
int v = to[i] ;
if(v==f) continue;
dfs1(v,x);
dp[x] += dp[v] + (w[i]==0);
}
}
void dfs2(int u,int fa)
{
for(int i=head[u];i;i=ne[i])
{
int v=to[i];
if(v==fa) continue;
if(w[i]==1) dp[v] = dp[u]+1;
else dp[v] = dp[u]-1;
dfs2(v,u);
}
}
void sol()
{
int n;
cin>>n;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
add(u,v,1);
add(v,u,0);
}
int ans=1e9;
dfs1(1,0);
dfs2(1,0);
vector<int> res;
for(int i=1;i<=n;i++)
{
if(dp[i]<ans)
{
ans = dp[i];
res.clear();
res.push_back(i);
}
else if(dp[i]==ans) res.push_back(i);
}
cout<<ans<<endl;
for(int i=0;i<(int)res.size();i++) cout<<res[i]<<' ';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int t;
t=1;
// cin>>t;
while(t--)
sol();
return 0;
}