1. 程式人生 > >Codeforces Round #247 (Div. 2) C. k-Tree

Codeforces Round #247 (Div. 2) C. k-Tree

*1600第二題,慢慢做,慢慢想。

題目連結:點選開啟原題目

題意:給一個數字k,用它構造一棵樹,構造方法是,從根節點開始往下,每個節點都有k個子節點(所以這棵樹的深度是無限的,一開始沒有讀到這一點一直想不通樣例),每條連線子節點的邊的權值從1~k,現在要求從根節點出發的所有路徑中,路徑權值總和為n,且路徑上至少有一條邊的權值大於等於d的方案有多少種。

思路:用 dp[i][j] 表示當前走的路徑的總和為 i,路徑中的最大的權值為 j 的方案數。那麼轉移方程是dp[i][j]+=dp[i-c][j],表示當前總和為i,最大值為j的情況,可以由上一步總和為i-c最大值還是j的情況,走一條權值為c的邊這種情況得到,將所有可能的情況加起來就是dp[i][j]的方案數。而我們最後要得到的是,最大為k的陣列成的,但是最小要有一個大小為d的邊,利用容斥定理可以知道,只需要用所有的方案數減去不合法的,也就是最大隻有d-1的陣列成的方案數就是我們最後要得到的答案(開long long 啊)。

AC程式碼:

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
long long  dp[110][110];//dp[i][j]表示已經選了i個數,最大的一個數是j
int main()
{
    int n,k,d;
    scanf("%d%d%d",&n,&k,&d);
    //用小於k的數湊成n。
    for(int i=1; i<=k; i++)
        dp[0][i]=1;
    for(int i=1; i<=n; i++) //列舉最後要得到的和
        for(int j=1; j<=k; j++) //列舉當前路徑中最大的值
            for(int c=1; c<=j; c++) //列舉當前能選的數,最大是k
            {
                if(i-c>=0)
                    dp[i][j]=(dp[i][j]+dp[i-c][j])%mod;
                else break;
            }
    printf("%lld\n",(dp[n][k]-dp[n][d-1]+mod)%mod);
    return 0;
}