zoukankan      html  css  js  c++  java
  • 长寿花:dp

    当然可以打组合数+CRT什么的,但是其实不必那么麻烦。

    先讲那个思路,再转化过来吧。

    首先可以发现的一个问题:所有颜色之间是没有区别的,所以我们其实并不在意到底是哪几种,我们只需要知道有几种就可以了。

    逐层递推:设dp[i][j]表示到了第i层,这层用了某j种颜色的总方案数。

    注意这里为了方便去重,所以dp的含义里是已经确定了j种颜色,所以不要乘上 $ C_m^j $

    那么再设s[i]是填到第i曾为止的总方案数那么s[i]=Σdp[i][j]*C[m][j]

    还需要预处理一下g数组,g[i][j]表示用恰好i种颜色填满j个格子的方案数。而这j中颜色也是未钦定的所以不带组合数。

    那么dp的转移式也就有了,dp[i][j]=(s[i-1]-dp[i-1][j])*g[j][a[i]];

    只从上一层转移而来所以可以滚动。

    f的预处理也很简单,转移就是从长度短1的状态中选择一种颜色填上。

    g[i][j]=g[i-1][j]*(j-1)+g[i-1][j-1]*j;即如果不新增颜色的话那么你能从已有的j种里除上一位刚填的那一种外其他的颜色里选一种,即j-1。

    如果你新开了一种颜色,那么就要决定这新开的一种颜色是现有的j种里的哪一种,即乘j。

    所以整体思路还是比较简单的。并没有调出我的CRT所以不粘这个代码。

    然后想一想简化:当模数不是质数时除法会出问题而加减乘都没事。然而整个代码里只有组合数那一步需要除法。

    那么怎么搞掉组合数呢?

    需要一些脑洞,我们新开一个数组f。f表示的和g数组差不多,但是是已经钦定了具体是哪j种颜色。

    所以g的转移就是f[i][j]=f[i-1][j-1]*(m+1-j)+f[i-1][j]*(j-1);

    如果不新增颜色的话那么和f是一样的转移,但是如果是新增了颜色,因为颜色需要确定,所以我们需要明确知道新加入的是哪种颜色。

    那就是在还没有被选过的颜色里选一个,一共m种已经用了j-1种,那么剩余的选择就是m-j+1。

    那么dp的含义也随之变化,dp亦表示已钦定后的值,那么就相当与乘上那个组合数了。

    再考虑dp转移。dp[i][j]=s[i-1]*f[j][a[i]]-dp[i-1][j]*g[j][a[i]];含义的话也类似。

    在总方案里我们是已钦定的所以本层到底是哪几种不重要,所以乘f。

    而在dp[i-1][j]里我们对于每一种方案里都已确定好颜色,每一个方案对应的本层都有特定的限制,所以需要钦定,用g乘。

    代码短多了。时间也快了。

    还有其实数组不用滚,用完直接覆盖就行。

     1 #include<cstdio>
     2 long long n,p,m,a[1000005],f[5005][5005],dp[5005],g[5005][5005],ld;
     3 main(){
     4     scanf("%lld%lld%lld",&n,&m,&p);for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
     5     f[0][0]=ld=g[0][0]=1;
     6     for(int i=1;i<=5000;++i) for(int j=i;j<=5000;++j)
     7         f[i][j]=(f[i][j-1]*(i-1)+f[i-1][j-1]*(m+1-i))%p,g[i][j]=(g[i][j-1]*(i-1)+g[i-1][j-1]*i)%p;
     8     for(int i=1;i<=n;++i,ld=dp[0],dp[0]=0) for(int j=1;j<=a[i]&&j<=m;++j)
     9         (dp[0]+=(dp[j]=(ld*f[j][a[i]]-(j<=a[i-1])*dp[j]*g[j][a[i]])%p))%=p;
    10     printf("%lld
    ",(ld+p)%p);
    11 }
    516B
  • 相关阅读:
    平衡二叉查找树——AVL树
    Java 输入输出(一)——流
    C++获取系统当前时间(精确到微秒)
    C++ STL中哈希表 hash_map介绍
    ubuntu下面编译libuv
    linux使用select实现精确定时器详解
    .dll,.lib,.def 和 .exp文件
    没有core文件时候如何定位segment/core dump
    C++中string、char *、char[]的转换
    map自定义结构体作为key
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11447830.html
Copyright © 2011-2022 走看看