Link:

BZOJ 1040 传送门

Solution:

基环树$dp$

如果仅仅是一棵树,直接树形$dp$即可,维护选与不选两种状态下的方案数

但此题是一个基环树,即除了一个环外是一个树形结构

 

对于环,一般都是将环转化为链处理

我们只需要删掉环上的任意一条边即可将环转化为树,那我们只需要人为判断这条边对答案的贡献就行了

设这条边是$(u,v)$,那么有2种情况

1.不选$u$,那么$v$选不选都行,以$u$为根跑一遍树形$dp$

2.不选$v$,那么$u$选不选都行,以$v$为根跑一遍树形$dp$

 

建图有两种方式:

Solution A:

先将原图建好,$dfs$找到返祖边,删除返祖边

Solution B:

在连边时用并查集维护,如果出现环则不连这条边

 

Solution B 跑得更快些

Code:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN=1e6+10;

bool vis[MAXN],flag=false;
int n,l,r,dat[MAXN],mat[MAXN];
ll dp[MAXN][2],res=0;
vector<int> G[MAXN];

void dfs(int x,int anc)
{
    vis[x]=true;
    for(int i=0;i<G[x].size() && !flag;i++)
    {
        int v=G[x][i];
        if(v==anc) continue;
        if(vis[v]) 
        {
            flag=true;
            G[x].erase(find(G[x].begin(),G[x].end(),v));
            G[v].erase(find(G[v].begin(),G[v].end(),x));
            l=x;r=v;break;
        }
        dfs(v,x);
    }
}

void Trdp(int x,int anc,int ban)
{
    vis[x]=true;
    if(x==ban) dp[x][1]=0;
    else dp[x][1]=dat[x];
    dp[x][0]=0;
    for(int i=0;i<G[x].size();i++)
    {
        int v=G[x][i];
        if(v==anc) continue;
        Trdp(v,x,ban);
        dp[x][0]+=max(dp[v][0],dp[v][1]);
        dp[x][1]+=dp[v][0];
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&dat[i],&mat[i]);
        G[i].push_back(mat[i]);G[mat[i]].push_back(i);
    }
    for(int i=1;i<=n;i++)
    {
        if(vis[i]) continue;
        ll t=0;flag=false;dfs(i,0);
        Trdp(l,0,r);t=max(dp[l][0],dp[l][1]);
        Trdp(r,0,l);t=max(t,max(dp[r][0],dp[r][1]));
        res+=t;
    }
    printf("%lld",res);
    return 0;
}
Solution A

相关文章: