类型:树形 DP

传送门:>Here<

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

解题思路

首先树形Dp是很明显的,$f[i][0]$表示$i$的子树中,$i$不参与匹配的最大匹配数,同样$f[i][1]$表示$i$参与匹配的最大匹配数。这样第一个子问题的答案就是$Max(f[1][0], f[1][1])$。

对于$f[i][0]$的转移很简单,既然$i$不参与匹配,那么$f[i][0]$就是它的每棵子树的最大匹配之和$$f[i][0]=\sum\limits_{ \ }Max(f[v][0],f[v][1])$$

对于$f[i][1]$,情况稍稍复杂一点。由于$i$节点最多只能被匹配一次,所以我们可以这样来考虑。对于节点$u$,选择其中一个子节点$v$来和自己匹配,剩余的仍然取最大值,这样剩余的最大值很显然是$f[u][0]-Max(f[v][0],f[v][1])$(刚才是用$Max(f[v][0],f[v][1])$推过来的),并且除了子节点$v$,$v$的其余儿子依然是独立的,所以再加上$f[v][0]$。所以转移方程即为$$f[u][1] = Max\{ f[u][0]-Max(f[v][0],f[v][1])+f[v][0]+1 \}$$

然后考虑第二个子问题,方案数怎么办?依然Dp。$g[i][k]$表示对应的$f[i][k]$的方案数。但注意了,答案不是$g[1][1]$,因为有可能$f[1][0]=f[1][1]$,这时候最大值有两个,所以答案可能是$g[1][0]+g[1][1]$。

还是先考虑$g[i][0]$如何转移,对于节点$u$,它由于是由各个子节点转移过来的,所以总的方案数就是各个子节点最大值方案数的积。还是一样,需要特判一下$g[v][0] =g[v][1]$的情况。

最难的也就是$g[i][1]$的转移了。对于这种情况,首先我们需要判断一下哪些节点与根匹配以后会转移出来最大值$f[u][1]$,而判断这个只需要按照前面的方程再推一遍看看是否相等就可以了。即$if(f[u][0]-Max(f[v][0],f[v][1])+f[v][0]+1 == f[u][1])$. 而这时候,我们还是仿照前面的思维过程,但是注意方案数中除去一部分使用除法而不是减法(前面是由乘法转移过来的),加上也要改成乘上。并且还是要特判$f[v][0]=f[v][1]的情况$。具体见代码。

想出上面这些并不复杂,而我却在$g$的初始化上卡了将近一小时。一开始我把每个$g[i][0]$和$g[i][1]$都初始化为$1$,结果挂了。调试千百遍之后把$g[i][1]$的初始化删掉以后突然就对了。这个现在我是这样理解的,因为我的$g[i][0]$是要拿去直接乘的,显然不能先赋成$0$,不然推出来的$g[i][0]$就全是$0$了。并且$g[i][1]$是加的,刚开始如果就是$1$就会影响结果。这个只不过是一个非常牵强的理解……

Code

高精度

/*written by -=qxz=- */
#include <cstdio>
#include <cctype>
#include <cstring>
#include <vector>
#include <algorithm>
#include <cassert>
#include <iostream>
#include <string>
#define  INF  (0x3f3f3f3f)
#define  Max(a,b)  ((a) > (b) ? (a) : (b))
#define  Min(a,b)  ((a) < (b) ? (a) : (b))
#define  N  (1010)
typedef long long ll;
using namespace std;  

struct BigInteger {
    typedef unsigned long long ll;  

    ;
    ;
    vector<int> s;  

    BigInteger& clean(){)s.pop_back(); return *this;}
    BigInteger(ll num = ) {*this = num;}
    BigInteger(string s) {*this = s;}
    BigInteger& operator = (long long num) {
        s.clear();
        do {
            s.push_back(num % BASE);
            num /= BASE;
        } );
        return *this;
    }
    BigInteger& operator = (const string& str) {
        s.clear();
        ) / WIDTH + ;
        ; i < len; i++) {
            int end = str.length() - i*WIDTH;
            , end - WIDTH);
            sscanf(str.substr(start,end-start).c_str(), "%d", &x);
            s.push_back(x);
        }
        return (*this).clean();
    }  

    BigInteger operator + (const BigInteger& b) const {
        BigInteger c; c.s.clear();
        , g = ; ; i++) {
             && i >= s.size() && i >= b.s.size()) break;
            int x = g;
            if (i < s.size()) x += s[i];
            if (i < b.s.size()) x += b.s[i];
            c.s.push_back(x % BASE);
            g = x / BASE;
        }
        return c;
    }
    BigInteger operator - (const BigInteger& b) const {
        assert(b <= *this);
        BigInteger c; c.s.clear();
        , g = ; ; i++) {
             && i >= s.size() && i >= b.s.size()) break;
            int x = s[i] + g;
            if (i < b.s.size()) x -= b.s[i];
            ) {g = -; x += BASE;} ;
            c.s.push_back(x);
        }
        return c.clean();
    }
    BigInteger operator * (const BigInteger& b) const {
        int i, j; ll g;
        vector<ll> v(s.size()+b.s.size(), );
        BigInteger c; c.s.clear();
        ;i<s.size();i++) ;j<b.s.size();j++) v[i+j]+=ll(s[i])*b.s[j];
        , g = ; ; i++) {
             && i >= v.size()) break;
            ll x = v[i] + g;
            c.s.push_back(x % BASE);
            g = x / BASE;
        }
        return c.clean();
    }
    BigInteger operator / (const BigInteger& b) const {
        assert(b > );
        BigInteger c = *this;
        BigInteger m;
        ; i >= ; i--) {
            m = m*BASE + s[i];
            c.s[i] = bsearch(b, m);
            m -= b*c.s[i];
        }
        return c.clean();
    }
    BigInteger operator % (const BigInteger& b) const {
        BigInteger c = *this;
        BigInteger m;
        ; i >= ; i--) {
            m = m*BASE + s[i];
            c.s[i] = bsearch(b, m);
            m -= b*c.s[i];
        }
        return m;
    }  

    int bsearch(const BigInteger& b, const BigInteger& m) const{
        , R = BASE-, x;
        ) {
            x = (L+R)>>;
            )>m) return x; else L = x;}
            else R = x;
        }
    }
    BigInteger& operator += (const BigInteger& b) {*this = *this + b; return *this;}
    BigInteger& operator -= (const BigInteger& b) {*this = *this - b; return *this;}
    BigInteger& operator *= (const BigInteger& b) {*this = *this * b; return *this;}
    BigInteger& operator /= (const BigInteger& b) {*this = *this / b; return *this;}
    BigInteger& operator %= (const BigInteger& b) {*this = *this % b; return *this;} 

    BigInteger& ; return *this;}
    BigInteger& ; return *this;}   

    bool operator < (const BigInteger& b) const {
        if (s.size() != b.s.size()) return s.size() < b.s.size();
        ; i >= ; i--)
            if (s[i] != b.s[i]) return s[i] < b.s[i];
        return false;
    }
    bool operator >(const BigInteger& b) const{return b < *this;}
    bool operator<=(const BigInteger& b) const{return !(b < *this);}
    bool operator>=(const BigInteger& b) const{return !(*this < b);}
    bool operator!=(const BigInteger& b) const{return b < *this || *this < b;}
    bool operator==(const BigInteger& b) const{return !(b < *this) && !(b > *this);}
};
ostream& operator << (ostream& out, const BigInteger& x) {
    out << x.s.back();
    ; i >= ; i--) {
        ];
        sprintf(buf, "%08d", x.s[i]);
        ; j < strlen(buf); j++) out << buf[j];
    }
    return out;
}
istream& operator >> (istream& in, BigInteger& x) {
    string s;
    if (!(in >> s)) return in;
    x = s;
    return in;
}
template <class T>
inline void read(T &x){
    , ch = ; x = ;
    ;
    ) + (x<<) + ch - ';
    if(f) x = -x;
}
int n,u,m,v;
vector <int> G[N];
];
BigInteger g[N][];
inline void AddEdge(int u, int v){
    G[u].push_back(v);
}
void DP(int u, int fa){
    ;
    ; i < sz; ++i){
        if(G[u][i] == fa) continue;
        ++not_leaf;
    }
    g[u][] = ;
    if(!not_leaf){
        return;
    }
    ; i < sz; ++i){
        v = G[u][i];
        if(v == fa) continue;
        DP(v,u);
        f[u][] += Max(f[v][], f[v][]);
        ] == f[v][]) g[u][] *= (g[v][] + g[v][]);
        else{
            ] > f[v][]) g[u][] *= g[v][];
            ] > f[v][]) g[u][] *= g[v][];
        }
    }
    f[u][] = f[u][];
    ; i < sz; ++i){
        v = G[u][i];
        if(v == fa) continue;
        f[u][] = Max(f[u][], f[u][]-Max(f[v][],f[v][])+f[v][]+);
    }
    ; i < sz; ++i){
        v = G[u][i];
        if(v == fa) continue;
        ]-Max(f[v][],f[v][])+f[v][]+ == f[u][]){
            ] == f[v][] && g[v][]+g[v][] != ){
                g[u][] += g[u][]/(g[v][]+g[v][])*g[v][];
            }
            else{
                ] > f[v][] && g[v][] != ){
                    g[u][] += g[u][]/(g[v][])*g[v][];
                }
                ] > f[v][] && g[v][] != ){
                    g[u][] += g[u][]/(g[v][])*g[v][];
                }
            }
        }
    }
}
int main(){
//    freopen(".in","r",stdin);
    read(n);
    ; i <= n; ++i){
        read(u);
        read(m);
        ; j <= m; ++j){
            read(v);
            AddEdge(u,v);
        }
    }
    DP(,);
    printf(][], f[][]));
    ][] == f[][]) cout << g[][]+g[][];
    else{
        ][] > f[][]) cout << g[][];
        ][] > f[][]) cout << g[][];
    }
    ;
}

[CEOI2007] 树的匹配Treasury的更多相关文章

  1. luogu P1623 [CEOI2007]树的匹配Treasury

    题目链接 luogu P1623 [CEOI2007]树的匹配Treasury 题解 f[i][0/1]表示当前位置没用/用了 转移暴力就可以了 code // luogu-judger-enable ...

  2. [CEOI2007]树的匹配Treasury(树形DP+高精)

    题意 给一棵树,你可以匹配有边相连的两个点,问你这棵树的最大匹配时多少,并且计算出有多少种最大匹配. N≤1000,其中40%的数据答案不超过 108 题解 显然的树形DP+高精. 这题是作为考试题考 ...

  3. bzoj5123 [Lydsy12月赛]线段树的匹配

    题意: 线段树是这样一种数据结构:根节点表示区间 [1, n]:对于任意一个表示区间 [l, r] 的节点,若 l < r, 则取 mid = ⌊l+r/2⌋,该节点的左儿子为 [l, mid] ...

  4. 【bzoj5123】[Lydsy12月赛]线段树的匹配 树形dp+记忆化搜索

    题目描述 求一棵 $[1,n]$ 的线段树的最大匹配数目与方案数. $n\le 10^{18}$ 题解 树形dp+记忆化搜索 设 $f[l][r]$ 表示根节点为 $[l,r]$ 的线段树,匹配选择根 ...

  5. CF308C-Sereja and Brackets-(线段树+括号匹配)

    题意:给出一段括号,多次询问某个区间内能匹配多少括号. 题解:线段树,结构体三个属性,多余的左括号l,多余的右括号r,能够匹配的括号数val. 当前结点的val=左儿子的val+右儿子的val+min ...

  6. BZOJ5123 线段树的匹配(树形dp)

    线段树的任意一棵子树都相当于节点数与该子树相同的线段树.于是假装在树形dp即可,记忆化搜索实现,有效状态数是logn级别的. #include<iostream> #include< ...

  7. bzoj千题计划164:bzoj5123: 线段树的匹配

    http://www.lydsy.com/JudgeOnline/upload/201712/prob12.pdf dp[len][0/1] 表示节点表示区间长度为len,节点选/不选的 最大匹配 s ...

  8. bzoj 5123: [Lydsy1712月赛]线段树的匹配

    设f[0/1][x]为区间[1,x]的根向下 不选(0)或者选(1)  的dp pair<最优值,方案数>. 可以很容易的发现总状态数就是log级别的,因为2*n 与 (2*n+1 或者 ...

  9. Linux DTS(Device Tree Source)设备树详解之二(dts匹配及发挥作用的流程篇)【转】

    转自:https://blog.csdn.net/radianceblau/article/details/74722395 版权声明:本文为博主原创文章,未经博主允许不得转载.如本文对您有帮助,欢迎 ...

随机推荐

  1. 二十:让行内元素在div中垂直居中

    (1)使用display:table-cell配合vertical-align:center(淘宝也是这样用的) <div class="method4"> <s ...

  2. 分享一个小设置-项目启动时服务器指向本地IIS

    背景,在X公司做的一个网站登录时需要域名的支持,就是说浏览器地址栏在localhost+端口号的形式下无法实现登录(必须是xxxx域名的形式), 但是很多时候都会先在线下进行测试,既然本地没有线上的环 ...

  3. 会议室预订系统(meeting room booking system)

    一.mrbs mrbs:(meeting room booking system) 二.效果   三.models from django.db import models # Create your ...

  4. haoop笔记

    : //:什么是hadoop? hadoop是解决大数据问题的一整套技术方案 :hadoop的组成? 核心框架 分布式文件系统 分布式计算框架 分布式资源分配框架 hadoop对象存储 机器计算 :h ...

  5. semantic-ui 图标

    semantic-ui提供了很多的图标,基本常用的在官网上面都能找到.要想记住这么多图标是不可能的,但是也是有简便方法记忆. 首先,图标其实和按钮的区别基本没有,要说有的话,也就是基础样式的大小不同吧 ...

  6. MySql数据库连接池专题

    MySql数据库连接池专题 - aspirant - 博客园https://www.cnblogs.com/aspirant/p/6747238.html

  7. java 工具

    JClassLib 4.2 发布了,该版本支持 Java 7 和 Java 8 的类文件属性查看. JClassLib不但是一个字节码阅读器而且还包含一个类库允许开发者读取,修改,写入Java Cla ...

  8. 基于vue-cli,sass,vant的移动端项目

    项目架构 开始 vue init webpack    项目名称         //新建项目,cd进入新项目 npm install axios                    //先安装! ...

  9. vue 动态插入组件

    HTML代码: <div id="app"> <p>{{ message }}</p> <button @click="add( ...

  10. (二)Wireshark的实用表格

    主要内容: 1.了解端点概念,学习如何在Wireshark中查询端点信息 2.学习利用端点窗口与会话窗口来分析数据包的特点 3.学会Wireshark的协议分层统计窗口的用法 一.端点概念 和数学里的 ...