【树DP】【基环树】[ZJOI2008][HYSBZ/BZOJ1040]骑士

题目链接

分析

看穿题目

如果一个骑士痛恨另一个骑士,就在两个骑士直接连接一条无向边。题目就是要求图中的最大权值独立集。

实现方法

如果这道题的图是一棵树(或森林)的话,显然可以用树形动态规划解决。
f(u) 表示以u为根的子树中的最大权值的独立集,令 s(u)=vuvf(v) gs(u)=vuvf(v)

f(u)=max(s(u),gs(u)+wt(u))

但是这道题的图并不是树,那怎么办呢?

这道题的图中,每一个连通块一定是“基环树”,即每个连通块有且只有一个环(边数=点数)。

那对于每个连通块,我们只需要找出环,然后在环上随意切掉一条边,这个连通块就成为了一棵树,可以做树dp了。
显然,这条边连接的两个点是不能同时被选择的,怎么限制这个条件呢?
我们只需要对这个连通块做两次dp,以这两个点为分别根,且强制不选择为根的那个点即可。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 1000000
int wt[MAXN+10],fa[MAXN+10],n,st,ed,fa2[MAXN+10];
long long ans,gs[MAXN+10],s[MAXN+10],f[MAXN+10];
bool vis[MAXN+10];
struct node{
    int v;
    bool f,vis;
    node *next,*back;
}edge[MAXN*2+10],*adj[MAXN+10],*ecnt=&edge[0],*cut;
void addedge(int u,int v){
    node *p=++ecnt;
    p->v=v;
    p->next=adj[u];
    adj[u]=p;
    p=p->back=++ecnt;
    p->v=u;
    p->next=adj[v];
    adj[v]=p;
    p->back=ecnt-1;
}
void Read(int &x){
    char c;
    while((c=getchar())&&c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while((c=getchar())&&c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
void read(){
    int t;
    Read(n);
    for(int i=1;i<=n;i++){
        Read(wt[i]),Read(t);
        addedge(i,t);
    }
}
void dfs(int u){
    vis[u]=1;
    for(node *p=adj[u];p;p=p->next){
        if(!p->vis){
            p->vis=p->back->vis=1;
            if(vis[p->v]){
                st=p->v,ed=u;
                cut=p;
                continue;
            }
            fa[p->v]=u;
            dfs(p->v);
        }
    }
}
void dfs2(int u){
    gs[u]=s[u]=0;
    for(node *p=adj[u];p;p=p->next)
        if(!p->f&&p->v!=fa2[u]){
            fa2[p->v]=u;
            dfs2(p->v);
        }
    f[u]=max(gs[u]+wt[u],s[u]);
    s[fa2[u]]+=f[u];
    gs[fa2[fa2[u]]]+=f[u];
}
void solve(){
    int i;
    long long mx;
    for(i=1;i<=n;i++){
        mx=0;
        if(!vis[i]){
            dfs(i);
            cut->f=cut->back->f=1;
            fa2[st]=0;
            dfs2(st);
            mx=max(mx,s[st]);
            fa2[ed]=0;
            dfs2(ed);
            mx=max(mx,s[ed]);
            cut->f=cut->back->f=0;
        }
        ans+=mx;
    }
}
int main()
{
    read();
    solve();
    printf("%lld\n",ans);
}

你可能感兴趣的:(C++,动态规划,bzoj,ZJOI,基环树)