【ZJOI2008】【BZOJ1040】【LOJ10162】骑士

【题目链接】

【前置技能】

  • 树形DP

【题解】

  • 题意是:给出一个环套树森林,求最大独立集。
  • 先考虑如何做树上的最大独立集的DP,这个问题比较基础。 f [ p o s ] [ 0 / 1 ] f[pos][0/1] f[pos][0/1]表示以 p o s pos pos为根的子树中选了/没选节点 p o s pos pos的答案。转移方程 f [ p o s ] [ 1 ] = ∑ f [ s o n ] [ 0 ] f[pos][1] = \sum f[son][0] f[pos][1]=f[son][0] f [ p o s ] [ 0 ] = ∑ m a x { f [ s o n ] [ 0 ] , f [ s o n ] [ 1 ] } f[pos][0] = \sum max\{f[son][0],f[son][1]\} f[pos][0]=max{f[son][0],f[son][1]}
  • 环套树一般来说环上有一条边是不必要的。考虑对于环上的一条边 &lt; u , v &gt; &lt;u, v&gt; <u,v>,我们将其去掉,分别以 u u u v v v为根做一次求树上的最大独立集的DP。因为 u , v u,v u,v之间有连边,所以答案中要么不存在 u u u,要么不存在 v v v,所以 a n s = m a x { f [ u ] [ 0 ] , f [ v ] [ 0 ] } ans = max\{f[u][0], f[v][0]\} ans=max{f[u][0],f[v][0]}
  • 时间复杂度 O ( N ) O(N) O(N)

【代码】

#include<bits/stdc++.h>
#define	INF	0x3f3f3f3f
#define	LL	long long
#define	MAXN	1000010
using namespace std;
struct Edg{int nxt, to;}E[MAXN << 1];
struct edg{int x, y;}e[MAXN];
int n, fa[MAXN], size[MAXN], cnt, w[MAXN], head[MAXN], CNT;
LL dp[MAXN][2], ans;

template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
    x = 0; int f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

int find(int x){
    if (fa[x] == x) return fa[x];
    else return (fa[x] = find(fa[x]));
}

int merge(int x, int y){
    int fx = find(x), fy = find(y);
    if (fx == fy) return 0;
    if (size[fx] > size[fy]) swap(fx, fy);
    fa[fx] = fy, size[fy] += size[fx];
    return 1;
}

void dfs(int pos, int dad){
    dp[pos][0] = 0, dp[pos][1] = w[pos];
    for (int i = head[pos]; i; i = E[i].nxt){
        int son = E[i].to;
        if (son != dad){
            dfs(son, pos);
            dp[pos][0] += max(dp[son][0], dp[son][1]);
            dp[pos][1] += dp[son][0];
        }
    }
}

void add(int u, int v){
    ++CNT;
    E[CNT].to = v;
    E[CNT].nxt = head[u];
    head[u] = CNT;
}

int main(){
    read(n);
    for (int i = 1; i <= n; ++i)
        fa[i] = i, size[i] = 1;
    for (int i = 1; i <= n; ++i){
        read(w[i]);
        int u; read(u);
        if (merge(u, i)) add(u, i), add(i, u);
        else e[++cnt].x = i, e[cnt].y = u;
    } 
    for (int i = 1; i <= cnt; ++i){
        dfs(e[i].x, 0);
        LL tmp = dp[e[i].x][0];
        dfs(e[i].y, 0);
        chkmax(tmp, dp[e[i].y][0]);
        ans += tmp; 
    }
    printf("%lld\n", ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值