P4977 毒瘤之神异之旅 [动态规划]

毒瘤之神异之旅

题目描述见链接 .


color{blue}{最初想法}

F[i,j]F[i, j] 表示将 jj 划分为 ii 份的方案数, F[i,j]=F[i1,jk]F[i, j] = sum F[i-1, j-k] .
发现这样会使得形如 1 1 3. 的方案算多次: 1 1 3, 1 3 1, 3 1 1.


color{red}{正解部分}

F[i,j]F[i, j] 表示将 ii 划分成 jj 份的方案数, 转移时不再一个一个数字添加, 而是整体往上叠,
可得 F[i,j]=F[i1,j1]+F[ij,j]F[i, j] = F[i-1, j-1] + F[i-j, j], 分别表示包含 11 和 不包含 11 的情况 .
初值: F[0,0]=1F[0, 0] = 1, 时间复杂度 O(N2)O(N^2) .

鉴于上方的 dpdp 方式, 统计答案不能按传统顺序统计, 那么怎么统计呢 ??

我们可以对数字 xx 单独考虑, 设 xx 出现 yy次 的方案为 num[x,y]num[x, y],
num[x,y]=xyxy+1num[x, y] = x至少出现y次的方案数 - x至少出现y+1的方案数 .
xy=F[Nxy,Ky]x至少出现y次的方案数 = F[N-xy, K-y] .

到这一步, 发现求出 num[x,y]num[x, y] 对统计答案好像并没有作用,
只需计算出 xy=F[Nxy,Ky]x至少出现y次的方案数 = F[N-xy, K-y] 即可.

于是答案等于 F[Nxy,Ky]xMsum F[N-xy, K-y]*x^M, 统计答案时间复杂度同样是 O(N2)O(N^2) .


color{red}{实现部分}

#include<bits/stdc++.h>
#define reg register

const int maxn = 5005;
const int mod = 1e9 + 7;

int N;
int K;
int M;
int Ans;
int F[maxn][maxn];

int Ksm(int a, int b){ int s = 1; while(b){ if(b&1)s=1ll*s*a%mod; a=1ll*a*a%mod; b>>=1;} return s; }

int main(){
        scanf("%d%d%d", &N, &K, &M);
        F[0][0] = 1;
        for(reg int i = 0; i <= N; i ++) F[i][i] = 1;
        for(reg int j = 1; j <= K; j ++)
                for(reg int i = j; i <= N; i ++)
                        F[i][j] = (F[i-1][j-1] + F[i-j][j]) % mod;
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = 1; j <= K; j ++){
                        int num = 0;
                        if(i*j <= N) num += F[N-i*j][K-j];
                        int Amp = 1ll*num*Ksm(i, M) % mod;
                        Ans = (Ans + Amp) % mod;
                }
        printf("%d
", Ans);
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822509.html