BZOJ1040环加外向树森林上的动态规划

所有的边是无向的!

想一想也知道,只要有一个人讨厌另外一个人,这两个人最后是不会在一起的。

n个人n条边(图不保证联通),这就是一个环加外向树森林

不能同时取一条边上的两个人,用DP来实现

 

DFS找环,拆环x,y,分别讨论x不取和y不取

#include <iostream> 
#include <cstdio> 
#include <vector> 
#include <cstring> 
#define N 2000050 
    
using namespace std; 
typedef long long LL; 
vector<int> e[N],rec[N]; 
LL F[N][3],ans1,ans2,ans,V[N]; 
int fa[N],x,y,n; 
bool vis[N],fx[N]; 
    
int DFS(int u) { 
    vis[u] = true; 
    for (int i=0;i<e[u].size();i++) { 
        int v = e[u][i]; 
        if (v == -1) continue; 
        if (v == fa[u]) continue;  
        if (vis[v]) { 
            x = u , y = v; 
            e[u][i] = -1; 
            int rc = rec[u][i]; 
            e[v][rc] = -1; 
        } else { 
            fa[v] = u; 
            DFS(v);  
        } 
    } 
    return 0; 
} 
    
void treeDP(int u) { 
    vis[u] = true; 
    fx[u] = true; 
    for (int i=0;i<e[u].size();i++) { 
        int v = e[u][i]; 
        if (v == -1) continue; 
        if (!vis[v]) { 
            treeDP(v); 
            F[u][0] += max(F[v][0],F[v][1]); 
            F[u][1] += F[v][0]; 
        } 
    } 
    F[u][1] += V[u]; 
} 
    
int main() 
{ 
    //freopen("knight.in","r",stdin); 
    //freopen("knight.out","w",stdout);  
    scanf("%d",&n); 
    for (int i=1;i<=n;i++) { 
        scanf("%lld%d",&V[i],&x); 
        e[i].push_back(x); 
        rec[i].push_back( (int)e[x].size() ); 
        e[x].push_back(i); 
        rec[x].push_back( (int)e[i].size() - 1 );  
    } 
        
    for (int d=1;d<=n;d++) if (!fx[d]) { 
        //memset(fa,0,sizeof(fa)),x = y = 0; 
        //memset(vis,0,sizeof(vis)); 
        DFS(d); 
          
        ans1 = ans2 = 0LL; 
        
        memset(F,0,sizeof(F)); 
        memset(vis,0,sizeof(vis)); 
        treeDP(x); 
        ans1 = F[x][0]; 
                   
        memset(F,0,sizeof(F)); 
        memset(vis,0,sizeof(vis)); 
        treeDP(y); 
        ans2 = F[y][0]; 
        
        LL tmp = max(ans1,ans2); 
        ans += tmp; 
    }  
    printf("%lld\n",ans); 
    //fclose(stdin);fclose(stdout); 
    return 0; 
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值