码迷,mamicode.com
首页 > 其他好文 > 详细

【bzoj1040】骑士[ZJOI2008](树形dp)

时间:2018-01-07 23:29:42      阅读:215      评论:0      收藏:0      [点我收藏+]

标签:root   line   答案   hid   min   math   vector   解决   www.   

  题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1040

  这道题,很明显根据仇恨关系构造出的图形是一堆环套树。如果是普通的树,就可以马上裸树形dp了,于是我们先解决这个环的问题。所以要先把环找出来。

  找出环后,假如断掉一条环边,环套树就变成了普通的树,而我们可以直接对这棵树进行dp,但是要控制被断掉的边的两端不被选择。

  对断边形成的树进行dp的时候,我们的dp方程是这样表示的:f[i][0/1]表示结点i不选/选。

  假设断掉的两条边两端的结点是x和y,然后我们可以发现:当以x为根时,f[x][0]包含了不选x,选/不选y的情况;而当以y为根时,f[y][0]包含了不选y,选/不选x的情况。把这两种情况取个max,刚好就绕过了既选x,又选y的情况。

  然后把每棵环套树的答案加起来即可。

  PS:QAQ……细节调了快两个小时……

  代码:

技术分享图片
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#define ll long long
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
ll read()
{
    ll tmp=0; char f=1,c=getchar();
    while(c<0||9<c){if(c==-)f=-1; c=getchar();}
    while(0<=c&&c<=9){tmp=tmp*10+c-0; c=getchar();}
    return tmp*f;
}
using namespace std;
int fir[1000010],to[2000010],ne[2000010];
int a[1000010];
bool vis[1000010];
ll f[1000010][2];
int n,root,del,tot=0;
ll ans=0;
void add(int x,int y){to[tot]=y; ne[tot]=fir[x]; fir[x]=tot++;}
void dfs(int now,int pa)
{
    vis[now]=1;
    for(int i=fir[now];i>=0;i=ne[i])
        if(i!=(pa^1)){
            if(vis[to[i]]){
                root=to[i]; del=i^1; continue;
            }
            dfs(to[i],i);
        }
}
void dp(int now,int pa)
{
    f[now][1]=a[now]; f[now][0]=0;
    for(int i=fir[now];i>=0;i=ne[i])
        if(i!=(pa^1)&&i!=del&&i!=(del^1)){
            dp(to[i],i);
            f[now][1]+=f[to[i]][0];
            f[now][0]+=max(f[to[i]][0],f[to[i]][1]);
        }
}
void work()
{
    dfs(root,-1);
    dp(root,-1);
    ll tmp=f[root][0];
    dp(to[del],-1);
    ans+=max(tmp,f[to[del]][0]);
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)fir[i]=-1;
    for(int i=1;i<=n;i++){
        int x=read(),y=read();
        a[i]=x; add(i,y); add(y,i);
    }
    for(int i=1;i<=n;i++)
        if(!vis[i])root=i,work();
    printf("%lld",ans);
}
bzoj1040

  总结:遇到环套树的题,或者先断掉一条环边处理树,再把边的情况考虑进去;或者先处理环上的子树,再考虑整个环。

【bzoj1040】骑士[ZJOI2008](树形dp)

标签:root   line   答案   hid   min   math   vector   解决   www.   

原文地址:https://www.cnblogs.com/quzhizhou/p/8232071.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!