Codeforces Round #100 E. New Year Garland

好题让我刷新很多对dp状态的观念。

image

1、先让我们不考虑第二个条件,也就是相邻两层颜色集合不同。

那么显然很容易得到答案为i=1n(m1)l[i]1m

为什么呢其实显然,一层第一个位置可以填m个颜色,后面每个位置不能与前面位置相同,

则填m1,一层总和就是(m1)l[i]1m

但是先在多了一个条件相邻两层所选集合不能相同。

2、那么怎么办?很容易想到DP,g(i)表示前i层满足条件的所有方案数

容易得到一个错误的转移方程g(i)=g(i1)((m1)l[i]1m)g(i1)

这个方程的意思是g(i)第i层选了所有能选的方案数,然后再减去g(i-1)即 i和i-1层冲突的方案数

有两个错误

(1)ii1层冲突的方案数并不是g(i1),因为i1层与i层除了一一对应相等,调换顺序也可以,因为只要集合一样即可。

(2)假如说l[i1]>l[i]的话那么g[i1]包括一些颜色集合大小可能大于第i层的,那么一定会多减去一部分。

根据上面两个错误,我们能反推出一道新题目。

即l[i]满足单调,且相邻层不相等的方案数,就可以用前面的简单递推了。

3、前面说了那么多,无不指向一点——————我们需要多给g(i)加一维,即用了多少颜色,这要我们就能很方便的处理 2、(1)(2)内的两个问题了

(1)既然有了颜色一维,就可以f[l[i]][j](jm)j!算出这i与i-1层冲突的方案数。
f[i][j]ij

(2)以为有了颜色数量这一层,那么我们减去i与i-1层冲突层的数量时候,可以根据i的颜色数量来减去。

这样就很好解决了前两个问题。

4、回到前面的f[i][j],做这题的时候我才发现一直把这东西搞混了

f[i][j]=f[i1][j1]+(j1)f[i1][j]其实求的是颜色无标号的方案。
f[i][j]ij

为什么呢?假设有编号,首先这样推出的选择序列一定时颜色小的出现在颜色大的前面。

这样的序列就对应无标号方案,假设有标号那么

存在12234,13342这样的相同的情况,总会出现第一个小于另一种情况的方案不会算上。

不同无标号的方案也一定算一个答案。

所有以后题目:有5种颜色,染色1000个地盘,相邻的不能同种颜色,问选其中j种颜色染色方案数。
不能求完f[1000][j](j5)就完了还得乘上f[1000][j]j!(j5)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6+10,M = 5010;
int f[M][M],l[N],g[2][M];
int n,m,MOD,mi[N],jie[N];
void init(){
    f[0][0] = 1;
    for(int i = 1;i<M;++i){
        for(int j = 1;j<=i;++j){
            f[i][j] = (f[i-1][j-1]+(LL)f[i-1][j]*(j-1))%MOD;
        }
    }
    mi[0] = 1;
    for(int i = 1;i<=m;++i){
        mi[i] = (LL)mi[i-1]*(m-i+1)%MOD;
    }
    jie[0] = 1;
    for(int i = 1;i<N;++i)jie[i] = (LL)jie[i-1]*i%MOD;
}
int main(){
    // freopen("a.txt","r",stdin);
    scanf("%d%d%d",&n,&m,&MOD);
    init();
    for(int i = 1;i<=n;++i)scanf("%d",&l[i]);
    int s = 1;
    for(int i = 1;i<=n;++i){
        for(int j = 1;i>=2&&j<=l[i-2];++j){
            g[i&1][j] = 0;
        }
        int mn = min(m,l[i]);
        for(int j = 1;j<=mn;++j){
            g[i&1][j] = 
            (LL)mi[j]*f[l[i]][j]%MOD*s%MOD
            - (LL)g[(i-1)&1][j]*jie[j]%MOD*f[l[i]][j]%MOD;
            g[i&1][j]%=MOD;
        }
        s = 0;
        for(int j = 1;j<=mn;++j){
            s += g[i&1][j];
            s %= MOD;
        }
    }
    s = (s%MOD+MOD)%MOD;
    cout<<s<<'\n';
    return 0;
}
posted @   wanghai673  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示