noip模拟赛 hungary

 

分析:比较难的一道题,看到要求方案数,又是在一棵树上,自然就想起了树形dp.状态该怎么表示呢?首先肯定有一维状态表示以i为根的子树,考虑到i有没有匹配对答案也是有影响的,自然而然状态就出来了:f[i][0/1]表示以i为根的子树中,i取或不取的最大匹配.因为要求方案数,再开一个数组g[i][0/1]记录方案数.

      接下来考虑怎么转移.如果i不选,那么它的子节点不管选不选都没关系:

f[i][0] = Σmax{f[j][0],f[j][1]},如果i要选,那么它的子节点中一定有一个没选的点k,其它点随意.

f[i][1] = max{f[k][0] + 1 + Σmax{f[j][0],f[j][1] (j != k)}}.对于g的转移就是比较常见的套路了,子树内加法原理,子树合并乘法原理.每次选肯定要选最大匹配的那种方案,所以开一个数组ans,如果f[i][0] > f[i][1],ans[i] = g[i][0]; f[i][0] < f[i][1],ans[i] = g[i][1];

f[i][0] = f[i][1],ans[i] = g[i][1] + g[i][0]. 那么g[i][0] = πans[j],i要选的话,k就不能直接用ans的值,那么g[i][1] = Σ(g[k][0] * (πans[j] / ans[k])),涉及到取模,用到了乘法逆元.

设计状态的时候想清楚当前点有哪几种状态,它们对答案有没有影响.转移的时候想想转移过来的子节点必须满足什么要求,其它的点该怎么分配.求方案数的时候要分清楚是乘法原理还是加法原理.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const int maxn = 100010, mod = 1000000007;

typedef long long ll;

int T, P, n, head[maxn], to[maxn * 2], nextt[maxn * 2], tot = 1;
ll f[maxn][2], g[maxn][2], ans[maxn];

void add(int x, int y)
{
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;
}

ll qpow(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = (res * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return res;
}

void dfs(int x, int fa)
{
    f[x][0] = f[x][1] = ans[x] = 0;
    g[x][0] = g[x][1] = 1;
    bool flag = false;
    ll sum = 0, mul = 1;
    for (int i = head[x]; i; i = nextt[i])
    {
        int v = to[i];
        if (v != fa)
        {
            dfs(v, x);
            sum += max(f[v][0], f[v][1]);
            sum %= mod;
            f[x][0] += max(f[v][0], f[v][1]);
            f[x][0] %= mod; 
            g[x][0] = g[x][0] * ans[v] % mod;
            mul = mul * ans[v] % mod;
            flag = true;
        }
    }
    if (!flag)
        g[x][1] = 0;
    for (int i = head[x]; i; i = nextt[i])
    {
        int v = to[i];
        if (v != fa)
        {
            if (f[x][1] < f[v][0] + 1 + sum - max(f[v][1], f[v][0]))
            {
                f[x][1] = (((f[v][0] + 1 + sum) % mod) - max(f[v][1], f[v][0]) + mod) % mod;
                g[x][1] = g[v][0] * mul % mod * qpow(ans[v],mod - 2) % mod;
            }
            else
                if (f[x][1] == f[v][0] + 1 + sum - max(f[v][1], f[v][0]))
                    g[x][1] = (g[x][1] + g[v][0] * mul % mod * qpow(ans[v],mod - 2) % mod) % mod;
        }
    }
    if (f[x][0] > f[x][1])
        ans[x] = g[x][0];
    if (f[x][0] < f[x][1])
        ans[x] = g[x][1];
    if (f[x][0] == f[x][1])
        ans[x] = g[x][0] + g[x][1];
    ans[x] %= mod;
}

int main()
{
    scanf("%d%d", &T, &P);
    while (T--)
    {
        memset(head, 0, sizeof(head));
        tot = 1;
        scanf("%d", &n);
        for (int i = 1; i < n; i++)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            add(u, v);
            add(v, u);
        }
        dfs(1, 0);
        printf("%lld ", max(f[1][0], f[1][1]));
        if (P == 2)
            printf("%lld", ans[1] % mod);
        printf("\n");
    }
return 0;
}

 

posted @ 2017-10-24 18:49  zbtrs  阅读(182)  评论(0编辑  收藏  举报