zoukankan      html  css  js  c++  java
  • 【bzoj1040】骑士[ZJOI2008](树形dp)

      题目传送门: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

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

  • 相关阅读:
    【HNOI2016模拟4.14】B
    【NOIP2010提高组】引水入城
    【NOIP2010提高组】机器翻译
    【NOIP2010提高组】乌龟棋
    【NOIP2010提高组】关押罪犯
    【USACO题库】3.3.2 Shopping Offers商店购物
    【USACO题库】3.1.5 Contact联系
    【USACO题库】3.1.4 Shaping Regions形成的区域
    【USACO题库】3.1.3 Humble Numbers丑数
    c语言学习——printf格式规定符
  • 原文地址:https://www.cnblogs.com/quzhizhou/p/8232071.html
Copyright © 2011-2022 走看看