• CEOI2007 Treasury


    题目描述

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

    解法

    容易想到将边的匹配转换到点上面,用 (dp_{u,0/1}) 表示以节点 (u) 为根节点的子树中,不选/选 (u) 节点来匹配得到的最大匹配数。

    那么当 (u) 没有被选时,子节点选不选都无所谓

    我们令

    [f_u=max{dp_{u,0},dp_{u,1}} ]

    则有

    [dp_{u,0}=sum_{vin son(u)} f_v ]

    若选择 (u),则只有被选的那条边的那个子节点必不能选,其他节点选不选无所谓,则

    [dp_{u,1}=max{dp_{u,0}-f_v+dp_{v,0}+1} ]

    第一问这样做已经可以了,难点在于第二问——记录方案。

    由第一问,我们得到启发,用 (g_{u,0/1}) 表示以节点 (u) 为根节点的子树中,不选/选 (u) 节点来匹配得到的最大匹配数的方案数。

    若不选择 (u) 节点,则由乘法原理将所有的子节点对应的方案的方案数乘起来即可

    [h_u=egin{cases} g_{u,0}, & dp_{u,0}>dp_{u,1} \ g_{u,0}+g_{u,1}, & dp_{u,0}=dp_{u,1}\ g_{u,1}, & dp_{u,0}<dp_{u,1} end{cases}]

    [g_{u,0}=prod_{vin son(u)} h_v ]

    而对于 (g_{u,1}) 我们需要在更新 (dp_{u,1}) 的时候同时更新它。假设我们当前选的边为 (u o v) ,那么除了 (v) 之外的其它子节点都可以任意选,而不选 (v) 节点对应的方案数为 (g_{v,0}),那么总的方案数为 (g_{u,0}/h_v imes g_{v,0})。若 (dp_{u,1}<dp_{u,0}-f_v+dp_{v,0}+1),即
    我们找到了一个更大的匹配,那么直接将 (g_{u,1}) 更新为 (g_{u,0}/h_v imes g_{v,0});若 (dp_{u,1}=dp_{u,0}-f_v+dp_{v,0}+1),即我们找到了一个和最大匹配方案数相同的方案,那么将 (g_{u,1}) 累加上这个值即可。

    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        ll ret=dp[u][0]-f[v]+dp[v][0]+1;
        ll tmp=g[u][0]/h[v]*g[v][0];
        if(dp[u][1]<ret) dp[u][1]=ret,g[u][1]=tmp;
        else if(dp[u][1]==ret) g[u][1]+=tmp;
    }
    

    Tips

    我们发现最后的方案数超过了 (long long),需要些高精。然而方程中带了除法QAQ,此时需要一些转换。观察方程,发现所求值是一个总积除上中间一个数的形式,实际上可以把其转换为一个前缀积和一个后缀积相乘,完美的避开了除法。(如果你不想再重载减法的话,也可以把其转换为前缀和加后缀和)

    #include<stdio.h>
    #include<string.h>
    #include<vector>
    using namespace std;
    #define N 1007
    
    inline int read(){
        int x=0,flag=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
        return flag? x:-x;
    }
    
    inline int max(int x,int y){return x>y? x:y;}
    
    struct BigNum{
        int n,s[207];
        
        BigNum(int x=0){
            n=0;
            memset(s,0,sizeof(s));
            while(x) s[++n]=x%10,x/=10;
        }
        
        BigNum operator *(const BigNum B){
            BigNum c;
            c.n=B.n+n-1;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=B.n;j++){
                    c.s[i+j-1]+=s[i]*B.s[j];
                    c.s[i+j]+=c.s[i+j-1]/10;
                    c.s[i+j-1]%=10;
                }
            while(c.s[c.n+1]) c.n++;
            while(c.n>=1&&(!c.s[c.n])) c.n--;
            return c;
        }
        
        BigNum operator +(const BigNum B){
            BigNum c;
            c.n=max(B.n,n);
            for(int i=1;i<=c.n;i++){
                c.s[i]+=s[i]+B.s[i];
                c.s[i+1]=c.s[i]/10;
                c.s[i]%=10;
            }
            while(c.s[c.n+1]) c.n++;
            while(c.n>=1&&(!c.s[c.n])) c.n--;
            return c;
        }
        
        bool operator <(const BigNum B){
            if(n!=B.n) return n<B.n;
            for(int i=n;i>=1;i--)
                if(s[i]!=B.s[i]) return s[i]<B.s[i];
            return 0;
        }
        
        bool operator ==(BigNum B){
            return !(((*this)<B)|(B<(*this)));
        }
        
        void print(){
            if(!n) putchar('0');
            else for(int i=n;i>=1;i--) putchar(s[i]+'0');
            putchar('
    ');
        }
        
    }f[N],g[N][2],h[N],dp[N][2],pre[N],suf[N],pres[N],sufs[N];
    
    int n;
    bool vis[N];
    vector<int> G[N];
    
    void dfs(int u){
        vis[u]=1,g[u][0]=BigNum(1);
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i];
            if(!vis[v]) dfs(v);
            dp[u][0]=dp[u][0]+f[v];
            g[u][0]=g[u][0]*h[v];
        }
        pre[0]=BigNum(1);
        pres[0]=BigNum(0);
        for(int i=0;i<G[u].size();i++)
            pres[i+1]=pres[i]+f[G[u][i]],
            pre[i+1]=pre[i]*h[G[u][i]];
        suf[G[u].size()+1]=BigNum(1);
        sufs[G[u].size()+1]=BigNum(0);
        for(int i=G[u].size()-1;~i;i--)
            sufs[i+1]=sufs[i+2]+f[G[u][i]],
            suf[i+1]=suf[i+2]*h[G[u][i]];
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i];
            BigNum r(1);
            BigNum ret=pres[i]+sufs[i+2]+dp[v][0]+r;
            BigNum tmp=pre[i]*suf[i+2]*g[v][0];
            if(dp[u][1]<ret) dp[u][1]=ret,g[u][1]=tmp;
            else if(dp[u][1]==ret) g[u][1]=g[u][1]+tmp;
        }
        if(dp[u][0]<dp[u][1]) f[u]=dp[u][1],h[u]=g[u][1];
        else if(dp[u][0]==dp[u][1]) f[u]=dp[u][0],h[u]=g[u][0]+g[u][1];
        else f[u]=dp[u][0],h[u]=g[u][0];
    }
    
    signed main(){
        freopen("tree.in","r",stdin);
        freopen("tree.out","w",stdout);
        n=read();
        for(int i=1;i<=n;i++){
            int u=read(),op=read();
            for(int i=1;i<=op;i++)
                G[u].push_back(read());
        }
        BigNum ans(0),ret(1);
        for(int i=1;i<=n;i++){
            if(!vis[i]) dfs(i);
            if(ans<f[i]) ans=f[i],ret=h[i];
        }
        ans.print(),ret.print(); 
    }
    
  • 相关阅读:
    搭建angularjs API文档站点
    Web网站数据”实时”更新设计
    3kb jQuery代码搞定各种树形选择。
    阿里妈妈自动登录程序
    Android中RelativeLayout属性详细说明
    jQuery选择器总结
    ajax页面加载进度条插件
    jquery ajax 方法及各参数详解
    使用jQuery加载html页面到指定的div
    C#中无边框窗体移动
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/13869496.html
Copyright © 2020-2023  润新知