[BZOJ 1040] 骑士

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
#include <bits/stdc++.h>

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

vector<int> G[MAXN];
ll dp[MAXN][2],res=0,t=0;
int f[MAXN],n,x,dat[MAXN],l[MAXN],r[MAXN],cnt=0;

int find(int x){return (x==f[x])?x:(f[x]=find(f[x]));}
void Trdp(int x,int anc)
{
    dp[x][0]=0;dp[x][1]=dat[x];
    for(int i=0;i<G[x].size();i++)
    {
        int v=G[x][i];
        if(v==anc) continue;
        Trdp(v,x);
        dp[x][0]+=max(dp[v][1],dp[v][0]);
        dp[x][1]+=dp[v][0];
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&dat[i],&x);
        if(find(i)!=find(x))
        {
            G[x].push_back(i);G[i].push_back(x);
            f[f[i]]=f[x];
        }
        else l[++cnt]=i,r[cnt]=x;
    }
    for(int i=1;i<=cnt;i++)
    {
        Trdp(l[i],0);t=dp[l[i]][0];
        Trdp(r[i],0);t=max(t,dp[r[i]][0]);
        res+=t;
    }
    printf("%lld",res);
    return 0;
}
Solution B

 

Review:

1、处理环的常用方式:将环转化为链

 

2、用并查集维护点集的方式寻找环的效率更高

 

posted @ 2018-06-30 18:42  NewErA  阅读(127)  评论(0编辑  收藏  举报