Codeforces 700B 贪心

题意

给一课n个节点的树, 给出2k个关键点, 将他们分成k对, 每一对u, v产生dis(u, v)的贡献, 求最大贡献

题解

很明显能想到贪心, 但是贪心策略并不是那么显然.
考虑每条边的贡献, 肯定是让边一端的与另一边相连最优. 那么每条边造成min(siz[v], n - siz[v])的贡献, dfs一遍即可.

还有一种方法是考虑最大siz(这里指的是包含关键点的数量). 从根开始, 对于当前u的最大siz的子树v, 如果siz[v] > u的其他子树siz之和, 那么就可以从v里向其他子树的点相连, 因为是从根开始, 所以保证当前lca肯定最优. 然后其他子树都被匹配, 我们再去把还剩一些节点的最大子树v递归处理, 肯定还剩偶数个点数. 如果siz[v] < 其他子树siz之和, 那么一定存在一种方案当前就将所有点配对. 因为只需通过这种配对抵消的方式使所有子树都互相抵消只剩相同点数, 然后互相再相互抵消完毕.
对于当前是关键点的特判即可.

#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long lnt;
const int maxn = 2e5 + 5;
lnt ans, siz[maxn];
int n, m, num, mark;
int h[maxn];
struct edge
{
    int nxt, v;
}e[maxn * 2];
inline const int read()
{
    register int x = 0;
    register char ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x; 
}
inline void add(int u, int v)
{
    e[++num].v = v, e[num].nxt = h[u], h[u] = num; 
    e[++num].v = u, e[num].nxt = h[v], h[v] = num;
}
void dfs(int u, int fa)
{
    for (int i = h[u]; i; i = e[i].nxt)
    {
        int v = e[i].v;
        if(v == fa) continue;
        dfs(v, u);
        siz[u] += siz[v];
        ans += min(siz[v], m - siz[v]);
    }
}
int main()
{ 
    n = read(), m = read();
    m *= 2;
    int u, v;
    for (int i = 1; i <= m; ++ i) siz[u = read()] = 1;
    for (int i = 1; i < n; ++ i)
    {
        u = read(), v = read();
        add(u, v);
    }
    dfs(1, 1);
    printf("%lld\n", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值