Codeforces #219D: Choosing Capital for Treeland 题解

显然的树型dp题
发现一个性质,如果我的首都选在以i为根的子树内,那么无论选的是哪个点,以i为根的子树外面的边最终的指向都是一样的
我们先跑一边dfs,把每棵子树内的向上的边数和向下的边数记录下来
考虑dp[i]表示如果首都在以i为根的子树内,在以i为根的子树内需要翻转的最小边数
有两种转移:

  • 首都是i,此时需要翻转的边数就是以i为根的子树内向上的边数
  • 首都在i的某个孩子y的子树中,那么除了dp[y]的贡献,i的其他孩子的子树中的边都要向下,所以i的其他孩子的子树中的向上的边数要加入答案,i连向其他孩子的边如果有向上的,要加入答案,i连向y的边如果不是向上的,要记入答案

这样就能算出最小代价,题目中还要求打出所有可以作为首都的城市,我担心如果对每个节点记录一个所有可以作为首都的城市编号数组复杂度会炸,因为最坏情况下每个点都会出现在他的所有祖先的数组内,那么数组的总大小会是 O(n2) O ( n 2 ) 级别的(不过可能并不会有那么多的可行首都…)
我使用的方法是对于每个节点记录它是从哪些点转移来的,最后再跑一遍dfs把答案收集一下,因为每个节点只可能被他的父亲记录,所以记录的总点数是 O(n) O ( n )

#include <cassert>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,LL>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=100003;
const LL LINF=2e16;
const int INF=1e9;
const int magic=348;
const double eps=1e-10;
const double pi=acos(-1);

inline int getint()
{
    char ch;int res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

int n;
vector<Pair> v[200048];
int upnum[200048],downnum[200048];
int dp[200048];vector<int> trans[200048];

inline void dfs(int cur,int father)
{
    int i,y;upnum[cur]=downnum[cur]=0;
    for (i=0;i<int(v[cur].size());i++)
    {
        y=v[cur][i].x;
        if (y!=father)
        {
            dfs(y,cur);
            upnum[cur]+=upnum[y];downnum[cur]+=downnum[y];
            if (v[cur][i].y) downnum[cur]++; else upnum[cur]++;
        }
    }
}

inline void Dfs(int cur,int father)
{
    dp[cur]=upnum[cur];trans[cur].pb(0);
    int i,y,Up=0,Down=0,uu,dd,curu,curd;
    for (i=0;i<int(v[cur].size());i++)
    {
        y=v[cur][i].x;
        if (y!=father)
        {
            Dfs(y,cur);
            if (v[cur][i].y) Down++; else Up++;
        }
    }
    uu=upnum[cur]-Up;dd=downnum[cur]-Down;
    for (i=0;i<int(v[cur].size());i++)
    {
        y=v[cur][i].x;
        if (y!=father)
        {
            curu=uu-upnum[y];
            if (v[cur][i].y) curu+=Up; else curu+=Up-1;
            if (v[cur][i].y) curu++;
            if (dp[y]+curu<dp[cur])
            {
                dp[cur]=dp[y]+curu;
                trans[cur].clear();trans[cur].pb(y);
            }
            else if (dp[y]+curu==dp[cur])
                trans[cur].pb(y);
        }
    }
}

vector<int> anslist;
inline void getans(int cur)
{
    int i;
    for (i=0;i<int(trans[cur].size());i++)
        if (!trans[cur][i]) anslist.pb(cur); else getans(trans[cur][i]);
}

int main ()
{
    int i,x,y;
    n=getint();
    for (i=1;i<=n-1;i++)
    {
        x=getint();y=getint();
        v[x].pb(mp(y,1));v[y].pb(mp(x,0));
    }
    dfs(1,-1);Dfs(1,-1);
    printf("%d\n",dp[1]);
    getans(1);sort(anslist.begin(),anslist.end());
    for (i=0;i<int(anslist.size());i++) printf("%d ",anslist[i]);puts("");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值