题解 P2607 【[ZJOI2008]骑士】

题目链接

Solution [ZJOI2008]骑士

题目大意::给定基环树森林,求带权最大点独立集

分析:假如是一棵树的话我们朴素树形\(dp\)就可以搞定,板子题:没有上司的舞会

然后图是基环树森林,显然我们枚举每个联通块分别计算即可

那么怎么在基环树上做\(dp\),NOIP 2018旅行的思路,断环上一条边变成一棵树

但是我们没有必要枚举断所有的边,既然断边之后失去了这两个点不能同时选的信息,那我们可以人为分类讨论补上

我们任意断一条边后钦定一个点不能选,那么另一个点就可以随意选了,然后两个端点情况取最大值

存图的小\(trick:\)

如果从仇人向自己连边的话,那么每个点的入度为\(1\),原图就为外向树,可以省去很多判断

双倍经验:城市规划,这不过这题是无向边要加拓扑排序,基本操作

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 100;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
inline int read(){
    int x = 0;char c = getchar();
    while(!isdigit(c))c = getchar();
    while(isdigit(c))x = x * 10 + c - '0',c = getchar();
    return x;
}
vector<int> G[maxn];
int faz[maxn],vis[maxn],val[maxn],root,n;
ll f[maxn][2],ans;
inline void addedge(int from,int to){
    G[from].push_back(to);
    faz[to] = from;
}
void dfs(int u){
    f[u][0] = 0,f[u][1] = val[u];
    vis[u] = 1;
    for(int v : G[u]){
        if(v == root)continue;
        dfs(v);
        f[u][1] += f[v][0];
        f[u][0] += max(f[v][0],f[v][1]);
    }
}
inline ll solve(int now){
    ll res = 0;
    do{
        vis[now] = 1;
        now = faz[now];
    }while(!vis[now]);
    root = now;
    dfs(root);
    res = max(res,f[root][0]);
    root = faz[root];
    dfs(root);
    res = max(res,f[root][0]);
    return res;
}
int main(){
    n = read();
    for(int i = 1;i <= n;i++)
        val[i] = read(),addedge(read(),i);
    for(int i = 1;i <= n;i++)
        if(!vis[i])ans += solve(i);
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/colazcy/p/11649295.html