题解 P2028 【龙兄摘苹果】

· · 题解

!!!本题解主要讲算法,下列代码会hack掉,建议采用其他方法(如__int128这种黑科技) 看到这个题目肯定是要先抓住样例分析一波

n=4,k=2答案是:

{1}{2,3,4};{2}{1,3,4};{3}{1,2,4};{4}{1,2,3};
{1,2}{3,4};{1,3}{2,4};{1,4}{2,3}。

(思维敏锐的小伙伴肯定马上发现了规律)

好吧,那弱鸡的我们再看看可能和它有关系的数据量。

n=3,k=2

{1}{2,3};{2}{1,3};{3}{1,2};

看到这些例子有没有发现,上面的7组数据中,有6组都跟是在这组的基础上加一个4进去

{1}{2,3,4};{2}{1,3,4};{3}{1,2,4};
{1,2}{3,4};{1,3}{2,4};{1,4}{2,3}。

比如对于{1}{2,3}这组,在左边加个4,成了一组,右边加个4,又成了一组。

我们就能得到对于dp[i][j]来说(i表示框的数量,j表示装j个苹果)

那么dp[i][j]的一部分一定是从d[i][j-1]转换来的,怎么转换呢,就是再每组上放一个编号为j的苹果,而之前一共分成了i份,所以我们找到了第一部分

dp[i][j-1]*i;

然后,我们目标对准剩下的一组,{4}{1,2,3}我们发现:

在加入4之前,就是单纯的{1,2,3}这三组数,

所以这个也很好推导所有数据范围,可以得到另一部分是从把i-1个框内装j-1个苹果的基础上,我再单独放一个标号为i的苹果单独在一组。我们就也找到了第二部分

dp[i-1][j-1]

所以我们就找到了这样的动态转移方程(递推关系)

dp[i][j]=dp[i][j-1]*i+dp[i-1][j-1]

附代码,千万别提(chao)交(xi),虽然用了ull但是也不能过

#include <bits/stdc++.h>
using namespace std;
unsigned long long dp[10005][1005];//i个篮子,放入j个苹果 
unsigned long long n,k,p;
unsigned long long m(unsigned long long x)
{
    return x%p;
}
int main()
{
    scanf("%llu%llu%llu",&n,&k,&p);
    for (int i=1;i<=n;i++)
        dp[1][i]=1;
    for (int i=2;i<=k;i++)//篮子数量 
        for (int  j=i;j<=n;j++)//没有篮子为空,至少i个篮子有i个苹果 
        {
            if (j==i)
                dp[i][j]=1;
            else
                dp[i][j]=m(m(m(dp[i][j-1])*m(i))+m(dp[i-1][j-1])); 
        }
    printf("%llu\n",m(dp[k][n])); 
    return 0;
 }