bzoj1040: [ZJOI2008]骑士

题目
题解

树形DP,没有上司的舞会的模型
根据题目可以知道, 每一个联通块里有且只有一个环, 所以我们找到这个环然后从中间把它断开, 对断开的两个端点u1, u2, 分别舞会。
设dp[u][0]为不选u, dp[u][1]为选u,
那么这个联通块答案就是max(dp[u1][0], dp[u2][0])。
注意有好多联通块。对于所有联通块跑dp,加起来就是答案

我在这里解释一下为什么是max(dp[u1][0],dp[u2][0])
就拿样例来举例:
3
10 2
20 3
30 1
如果把2和3断开,那么就形成一条链2-1-3,如果是取了dp[u1][1]或dp[u2][1]的话,答案就会变成50,因为2和3都取了,但事实上不行,所以只能是max(dp[u1][0],dp[u2][0])

#include<bits/stdc++.h>
using namespace std;
const int N=1000002;
typedef long long ll;
struct node{
    int to,ne;
}e[N<<1];
ll ans,s,f[N][2];
int vis[N],n,x,y,h[N],w[N],tot=1,k,i;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
inline int read(){
    int x=0,fl=1;char ch=gc();
    for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
    for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
    return x*fl;
}
void add(int x,int y){
    e[++tot]=(node){y,h[x]};
    h[x]=tot;
}
void find(int u,int pre){
    vis[u]=1;
    for (int i=h[u],v;i;i=e[i].ne)
        if ((i^1)!=pre){
            if (vis[v=e[i].to]){
                x=u;y=v;k=i;
                continue;
            }
            find(v,i);
        }
}
void dfs(int u,int pre){
    f[u][0]=0;f[u][1]=w[u];
    for (int i=h[u],v;i;i=e[i].ne)
        if (i!=k && (i^1)!=k && (i^1)!=pre){
            dfs(v=e[i].to,i);
            f[u][1]+=f[v][0];
            f[u][0]+=max(f[v][0],f[v][1]);
        }
}
int main(){
    n=read();
    for (i=1;i<=n;i++) w[i]=read(),x=read(),add(i,x),add(x,i);
    for (i=1;i<=n;i++)
        if (!vis[i]){
            find(i,-1);
            dfs(x,-1);
            s=f[x][0];
            dfs(y,-1);
            ans+=max(s,f[y][0]);
        }
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/xumingyang0/article/details/80738825