【洛谷P2607】[ZJOI2008]骑士

骑士

题目链接

这道题一看,似乎和舞会是一样的,然而它并没有保证是一棵树

但是,对于每个连通块,必有相同的点数和边数,这样的图一定是一棵树上加一条边

这条边一定回使图中形成一个环,这种图貌似叫“基环树”。。

我们只要将不同的连通块分开处理,最后相加即可

对于一个基环树,只要找到环上的一条边,把它“拆掉”,分别从两个顶点dp一下就行了,

由于一条边的两个顶点不能同时选,就将f[u][0]与f[v][0]取一个max即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 1000100
int n,value[MAXN],Head[MAXN],num=1,root1,root2,cut=-1;
long long dp[MAXN][2],ans;
bool vis[MAXN];
struct NODE{
    int to,next;
} e[MAXN<<1];
inline int read(){
    int x=0; char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while('0'<=c&&c<='9') { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
    return x;
}
inline void add(int x,int y){
    e[++num].to=y;
    e[num].next=Head[x];
    Head[x]=num;
}
void dfs(int t,int last){  //找环
    vis[t]=1;
    for(int i=Head[t];i;i=e[i].next)
     if(e[i].to!=last){
        if(!vis[e[i].to]) dfs(e[i].to,t);
        else { cut=i; root1=t; root2=e[i].to; }
    }
}
void solve(int t,int last)  //舞会
{
    dp[t][0]=0;
    dp[t][1]=value[t];
    for(int i=Head[t];i;i=e[i].next)
     if(e[i].to!=last&&i!=cut&&i!=(cut^1))
      {
        int v=e[i].to;
        solve(v,t);
        dp[t][0]+=max(dp[v][1],dp[v][0]);
        dp[t][1]+=dp[v][0];
      }
}
int main()
{
    scanf("%d",&n);
    int y;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&value[i],&y);
        add(i,y); add(y,i); 
    }
    for(int i=1;i<=n;i++)
     if(!vis[i]){
        dfs(i,-1);
        solve(root1,-1);
        long long t=dp[root1][0];
        solve(root2,-1);
         ans+=max(t,dp[root2][0]);
     }
    printf("%lld
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/yjkhhh/p/9218052.html