zoukankan      html  css  js  c++  java
  • 洛谷1623 树的匹配 树形动态规划 高精度

    欢迎访问~原文出处——博客园-zhouzhendong

    去博客园看该题解


    题目传送门 - 洛谷1623


    题目描述

    题目描述

      给一棵树,你可以匹配有边相连的两个点,问你这棵树的最大匹配时多少,并且计算出有多少种最大匹配。

    输入输出格式

      输入格式:

        第一行一个数N,表示有多少个结点。

        接下来N行,每行第一个数,表示要描述的那个结点。然后一个数m,表示这个结点有m个儿子,接下来m个数,表示它的m个儿子的编号。

       【数据规模】

        N≤1000,其中40%的数据答案不超过108

      输出格式:

         输出两行,第一行输出最大匹配数,第二行输出最大匹配方案数。


    题解

      我们首先考虑一下这一题的数据范围~

      高精度。

      (猛啊出题人)


      为什么不可以人性化一点?

      那么既然这样,我们在设计算法的时候,就要尽量避免复杂的运算。

      我们用dp[i][0]代表在以第i个节点为根的子树里,根节点i不取,所能得到的最大匹配数;

      dp[i][1]代表在以第i个节点为根的子树里,根节点i要取,所能得到的最大匹配数;

      用sum[i][0]代表在以以第i个节点为根的子树里,根节点i不取,所能得到的最大匹配数的方案总数;

      dp[i][1]代表在以第i个节点为根的子树里,根节点i要取,所能得到的最大匹配数的方案总数;

      

      然后就是不断的转移。

      对于dp[i][0],我们有简单的转移,根节点不取,那么每个子树随意,那么就是所有子节点s的max(dp[s][0],dp[s][1])加起来。

      对于sum[i][0],我们如果一个都不选,那么显然是一种情况,然后每次选择子树的时候,我们要考虑3中情况。

      对于子树s,

      如果dp[s][0]>dp[s][1],那么我们会选择dp[s][0],那么sum[i][0]乘上sum[s][0]即可。

      如果dp[s][0]<dp[s][1],那么也同理,sum[i][0]乘上sum[s][1]即可。

      如果dp[s][0]=dp[s][1],那么,不管是s取或者不取的情况,都对当前答案有贡献,那么sum[i][j]乘上(sum[s][0]+sum[s][1])即可。

      比较麻烦的是dp[i][1]和sum[i][1]。

      对此,我们首先要保证有一个子节点s,dp[s][0]来和根节点匹配。

      那么其他的还是和刚才一样的,选大的。

      所以,我们可以求出前后缀dp以及前后缀sum,然后一个一个子节点搜过去,把答案分成3段,一段是左边,一段是右边,一段是枚举到的字节点。

      如果选择该子节点的匹配数大于当然得到的dp[i][1],那么当然更新dp[i][1],并重新累加sum[i][1];

      否则如果匹配数等于当前得到的dp[i][1],那么也对当前答案有贡献,累加即可。

      

      当然高精度也是卡了很多选手的地方。

      具体操作详见代码。

     


    代码

    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    using namespace std;
    typedef long long LL;
    const LL mod=10000000;
    struct LLLL{
        LL d,v[100];
        LLLL(){}
        LLLL(int x){
            d=0;
            memset(v,0,sizeof v);
            while (x){
                v[++d]=x%mod;
                x/=mod;
            }
        }
        void Print(){
            printf("%lld",v[d]);
            for (int i=d-1;i>=1;i--)
                printf("%07lld",v[i]);
        }
        LLLL operator +(const LLLL &x){
            LLLL Ans(0);
            Ans.d=max(d,x.d);
            for (int i=1;i<=Ans.d;i++)
                Ans.v[i]=v[i]+x.v[i];
            for (int i=1;i<=Ans.d;i++){
                Ans.v[i+1]+=Ans.v[i]/mod;
                Ans.v[i]%=mod;
            }
            if (Ans.v[Ans.d+1])
                Ans.d++;
            return Ans;
        }
        LLLL operator *(const LLLL &y){
            LLLL x=*this,Ans(0);
            for (int i=1;i<=x.d;i++)
                for (int j=1;j<=y.d;j++)
                    Ans.v[i+j-1]+=x.v[i]*y.v[j];
            Ans.d=x.d+y.d-1;
            for (int i=1;i<=Ans.d;i++){
                Ans.v[i+1]+=Ans.v[i]/mod;
                Ans.v[i]%=mod;
            }
            if (Ans.v[Ans.d+1])
                Ans.d++;
            return Ans;
        }
    };
    const int N=1000+5;
    vector <int> son[N];
    LLLL sum[N][2],sumL[N],sumR[N];
    int n,dp[N][2],maxL[N],maxR[N],in[N];
    void dfs(int rt){
        for (int i=0;i<son[rt].size();i++)
            dfs(son[rt][i]);
        dp[rt][0]=0;
        sum[rt][0]=1;
        for (int i=0;i<son[rt].size();i++){
            int s=son[rt][i];
            if (dp[s][0]>dp[s][1]){
                dp[rt][0]+=dp[s][0];
                sum[rt][0]=sum[rt][0]*sum[s][0];
            }
            if (dp[s][0]<dp[s][1]){
                dp[rt][0]+=dp[s][1];
                sum[rt][0]=sum[rt][0]*sum[s][1];
            }
            if (dp[s][0]==dp[s][1]){
                dp[rt][0]+=dp[s][1];
                sum[rt][0]=sum[rt][0]*(sum[s][0]+sum[s][1]);
            }
        }
        memset(sumL,0,sizeof sumL);
        memset(sumR,0,sizeof sumR);
        memset(maxL,0,sizeof maxL);
        memset(maxR,0,sizeof maxR);
        sumL[0]=1,sumR[son[rt].size()+1]=1;
        for (int i=1;i<=son[rt].size();i++){
            int s=son[rt][i-1];
            if (dp[s][0]>dp[s][1]){
                maxL[i]=maxL[i-1]+dp[s][0];
                sumL[i]=sumL[i-1]*sum[s][0];
            }
            if (dp[s][0]<dp[s][1]){
                maxL[i]=maxL[i-1]+dp[s][1];
                sumL[i]=sumL[i-1]*sum[s][1];
            }
            if (dp[s][0]==dp[s][1]){
                maxL[i]=maxL[i-1]+dp[s][0];
                sumL[i]=sumL[i-1]*(sum[s][0]+sum[s][1]);
            }
        }
        for (int i=son[rt].size();i>=1;i--){
            int s=son[rt][i-1];
            if (dp[s][0]>dp[s][1]){
                maxR[i]=maxR[i+1]+dp[s][0];
                sumR[i]=sumR[i+1]*sum[s][0];
            }
            if (dp[s][0]<dp[s][1]){
                maxR[i]=maxR[i+1]+dp[s][1];
                sumR[i]=sumR[i+1]*sum[s][1];
            }
            if (dp[s][0]==dp[s][1]){
                maxR[i]=maxR[i+1]+dp[s][0];
                sumR[i]=sumR[i+1]*(sum[s][0]+sum[s][1]);
            }
        }
        dp[rt][1]=0;
        sum[rt][1]=0;
        for (int i=1;i<=son[rt].size();i++){
            int s=son[rt][i-1];
            int v=dp[s][0]+maxL[i-1]+maxR[i+1]+1;
            if (v>dp[rt][1]){
                dp[rt][1]=v;
                sum[rt][1]=sum[s][0]*sumL[i-1]*sumR[i+1];
            }
            else if (v==dp[rt][1])
                sum[rt][1]=sum[s][0]*sumL[i-1]*sumR[i+1]+sum[rt][1];
        }
    }
    int main(){
        freopen("tree.in","r",stdin);
        freopen("tree.out","w",stdout);
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
            son[i].clear();
        memset(in,0,sizeof in);
        for (int i=1,bh,sz;i<=n;i++){
            scanf("%d%d",&bh,&sz);
            for (int j=1,s;j<=sz;j++)
                scanf("%d",&s),son[bh].push_back(s),in[s]++;
        }
        int rt=-1;
        for (int i=1;i<=n&&rt==-1;i++)
            if (in[i]==0)
                rt=i;
        dfs(rt);
        int ansv=max(dp[rt][0],dp[rt][1]);
        LLLL anssum(0);
        if (ansv==dp[rt][0])
            anssum=anssum+sum[rt][0];
        if (ansv==dp[rt][1])
            anssum=anssum+sum[rt][1];
        printf("%d
    ",ansv);
        anssum.Print();
        fclose(stdin);fclose(stdout);
        return 0;
    }
  • 相关阅读:
    接口和抽象类的异同点
    实体对象间传值克隆
    什么是反射&反射的8个类的用法
    ERP中反射的两个实例
    基础02 Java 跨平台原理
    基础01 dos命令
    lists删除
    多字段 java对象排序
    Java对象排序
    MySQL表中数据的迁移
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/LuoguP1623.html
Copyright © 2011-2022 走看看