1. 程式人生 > >【DP】【CF855C】 Helga Hufflepuff's Cup

【DP】【CF855C】 Helga Hufflepuff's Cup

Description

給你一個樹,可以染 \(m\) 個顏色,定義一個特殊顏色 \(k\) , 要求保證整棵樹上特殊顏色的個數不超過 \(x\) 個。同時,如果一個節點是特殊顏色,那麼它的相鄰節點的顏色編號必須全部小於 \(k\)。求方案數。

Input

第一行 \(n,m\) 代表節點個數和顏色樹

下面 \(n~-~1\) 行描述一棵樹

最後一行是特殊顏色 \(k\) 和顏色個數 \(x\)

Output

輸出一行一個整數,代表答案對 \(10^9~+~7\) 取模結果

Hint

\(Forall:\)

\(n~\leq~10^5~,~m~\leq~10^9~,~k~\leq~m~,~x~\leq~10\)

Solution

這題感覺有點像DDOSvoid的疑惑

我 爆 破 我 自 己

數數題,考慮DP。

顯然這個題的選點分為三種,分別是小於 \(k\),等於 \(k\),大於 \(k\)。同時有顏色個數的限制,於是可以設 \(f_{i,j,0/1/2}\) 代表以 \(i\) 為根的子樹,選了 \(j\) 個特殊顏色,其中節點 \(i\) 的顏色 小於/等於/大於 \(k\)

考慮一個節點只有兩個兒子的情況,直接把貢獻乘一下即可。

考慮一個節點有多個兒子的時候,每加入一個新節點產生的貢獻如何計算:

\(g_{i,j,0/1/2}\)\(u\) 的子樹(省略第一維),考慮前 \(i\)

個兒子,選了 \(j\)\(k\),節點 \(i\) 的狀態是 \(0/1/2\) 的方案數。

一下假設當前列舉的是 \(u\) 的第 \(i\) 個兒子。

考慮 \(u\) 選小於 \(k\) 的情況:

則該兒子可以選 \(0/1/2\) 三種情況,這三種情況相互並行,應該將貢獻相加,同時兩兩子樹間互不影響,應將貢獻相乘。

於是有

\[g_{i,j,0}~=~\sum_{h=0}^{j}(g_{i-1,j-h,0}~\times~\sum_{s=0}^{2}f_{to,h,s})\]

同理,\(u\) 選等於 \(k\) 的情況:

該兒子只能選 \(0\) 一種情況,於是有

\[g_{i,j,1}~=~\sum_{h=0}^{j}(g_{i-1,j-h,1}~\times~f_{to,h,0})\]

同理,\(u\) 選大於 \(k\) 的情況:

該兒子可以選 \(0/2\) 兩種可能,於是

\[g_{i,j,2}~=~\sum_{h=0}^{j}(g_{i-1,j-h,2}~\times~\sum_{s=0,2}f_{to,h,s})\]

\(u\)\(v\) 個孩子,於是

\[f_{u,j,0/1/2}~=~g_{v,j,0/1/2}\]

當然 \(g\) 可以把第一維滾掉,這樣就沒有空間問題辣~

於是就可以統計答案了。記得取模。程式碼裡面因為不知道哪裡爆掉了於是乾脆 \(define~~int~~ll\) 了23333

Code

#include <cstdio>
#include <cstring>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif
#define rg register
#define ci const int
#define cl const long long
#define int ll


typedef long long ll;

namespace IPT {
    const int L = 1000000;
    char buf[L], *front=buf, *end=buf;
    char GetChar() {
        if (front == end) {
            end = buf + fread(front = buf, 1, L, stdin);
            if (front == end) return -1;
        }
        return *(front++);
    }
}

template <typename T>
inline void qr(T &x) {
    rg char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
    if (lst == '-') x = -x;
}

template <typename T>
inline void ReadDb(T &x) {
    rg char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
    if (ch == '.') {
        ch = IPT::GetChar();
        double base = 1;
        while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
    }
    if (lst == '-') x = -x;
}

namespace OPT {
    char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
    if (x < 0) {x = -x, putchar('-');}
    rg int top=0;
    do {OPT::buf[++top] = x % 10 + '0';} while (x /= 10);
    while (top) putchar(OPT::buf[top--]);
    if (pt) putchar(aft);
}

const int maxn = 100010;
const int maxm = 200010;
const int MOD = 1000000007;

struct Edge {
    int to, nxt;
};
Edge edge[maxm]; int hd[maxn], ecnt = 1;
inline void cont(ci from, ci to) {
    Edge &e = edge[++ecnt];
    e.to = to; e.nxt = hd[from]; hd[from] = ecnt;
}

int n, m, x, v, dv;
int frog[maxn][15][3], gorf[maxn][2][15][3];

void reading();
void dfs(ci, ci);

signed  main() {
    freopen("1.in", "r", stdin);
    qr(n); qr(m);
    reading();
    qr(v); qr(x); dv = m - v;
    dfs(1, 0);
    int ans = 0;
    for (rg int i = 0; i <= x; ++i) 
        for(rg int j = 0; j < 3; ++j) ans = (ans + frog[1][i][j]) % MOD;
    qw(ans, '\n', true);
    return 0;
}

void reading() {
    int a, b;
    for (rg int i = 1; i < n; ++i) {
        a = b = 0;
        qr(a); qr(b);
        cont(a, b);
        cont(b, a);
    }
}

void dfs(ci u, ci pree) {
    int pre = 0;
    gorf[u][pre][0][0] = v - 1;
    gorf[u][pre][1][1] = 1;
    gorf[u][pre][0][2] = dv;
    for (int i = hd[u]; i; i = edge[i].nxt) if(i != pree) {
        int &to = edge[i].to;
        dfs(to, i ^ 1);
        pre ^= 1;
        memset(gorf[u][pre], 0, sizeof gorf[u][pre]);
        for (rg int j = 0; j <= x; ++j) {
            for (rg int k = 0; k <= j; ++k) {
                gorf[u][pre][j][0] = (gorf[u][pre][j][0] + 1ll * gorf[u][pre ^ 1][j - k][0] * (frog[to][k][0] + frog[to][k][1] + frog[to][k][2])) % MOD;
                gorf[u][pre][j][1] = (1ll * gorf[u][pre ^ 1][j - k][1] * frog[to][k][0] + gorf[u][pre][j][1]) % MOD;
                gorf[u][pre][j][2] = (1ll * gorf[u][pre ^ 1][j - k][2] * (frog[to][k][0] + frog[to][k][2]) + gorf[u][pre][j][2]) % MOD;
            }
        }
    }
    for (rg int i = 0; i <= x; ++i) 
        for (rg int j = 0; j < 3; ++j)
            frog[u][i][j] = gorf[u][pre][i][j];
#ifdef DEBUG
    printf("EM%d:\n", u);
    for (rg int i = 0; i <= x; ++i) {
        for (rg int j = 0; j < 3; ++j) 
            printf("%d %d %d\n",i, j, frog[u][i][j]);
    }
#endif
}

Summary

這類樹上求方案數的問題都需要在轉移時藉助一個輔助陣列,記錄已經列舉過得兒子的資訊,然後計算當前這個兒子加入的貢獻。