类型:树形 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. logstash安装及基础入门

    Logstash是一款开源的数据收集引擎,具备实时管道处理能力.简单来说,logstash作为数据源与数据存储分析工具之间的桥梁,结合 ElasticSearch以及Kibana,能够极大方便数据的处 ...

  2. python第七章:常用模块--小白博客

    yagmail模块 python标准库中发送电子邮件的模块比较复杂,因此,有许多开原的库提供了更加易用的接口来发送电子邮件,其中yagmail是一个使用比较广泛的开原项目,yagmail底层依然使用了 ...

  3. Python IO模型

    这篇博客是本人借鉴一些大神的博客并结合自己的学习过程写下的. 事件驱动模型 事件驱动模型是一种编程范式,这里程序的执行流由外部事件来决定.它的特点是包含一个事件循环,当外部事件发生时,不断从队列里取出 ...

  4. IOS 开发之-- textfield和textview,return键的改变,点击return键

    IOS 开发之-- textfield和textview,return键的改变,点击return键 一,textfield的return键改变 方案1.改变键盘右下角的换行(enter)键为完成键,后 ...

  5. Mysql 中的MVCC原理,undo日志的依赖

    一. MVCC 原理了解   原文点击:MVCC原理浅析 读锁: 也叫共享锁.S锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的 ...

  6. 【学习总结】C-翁恺老师-入门-第0周<程序设计与C>

    [学习总结]C-翁恺老师-入门-总 1-首先按视频说的下载编辑器 <DevC++> 并一路默认设置: 安装包下载链接 (我有vc6.0不过预感告诉我老师要用类似CS50里那种命令行编辑器? ...

  7. java的数据类型:基本数据类型和引用数据类型

    Java数据类型的基本概念 数据类型在计算机语言里面,是对内存位置的一个抽象表达方式,可以理解为针对内存的一种抽象的表达方式. 开始接触每种语言的时候,都会存在对数据类型的认识,有复杂的,有复杂的,各 ...

  8. CentOS 6.4 源码安装MySQL 5.6

    1.安装前准备工作 1.1 必备的包 gcc/g++ :MySQL 5.6开始,需要使用g++进行编译.cmake :MySQL 5.5开始,使用cmake进行工程管理,cmake需要2.8以上版本. ...

  9. java核心API学习

    1:java.lang  (Object.String.StringBuffer.Thread.System.ClassLoader.Class.Runtime.包装类等)

  10. 调整分区大小 转载--------------http://blog.csdn.net/perfectzq/article/details/73606119

    centos7重新调整分区大小 centos 7 调整 root 和 home 的容量大小 查看磁盘的空间大小: df -h  备份/home : cp -r /home/ homebak/ 卸载​  ...