Codeforces Round #100 E. New Year Garland
好题让我刷新很多对dp状态的观念。

1、先让我们不考虑第二个条件,也就是相邻两层颜色集合不同。
那么显然很容易得到答案为\(\prod\limits_{i=1}^{n}(m-1)^{l[i]-1} \cdot m\)
为什么呢其实显然,一层第一个位置可以填\(m\)个颜色,后面每个位置不能与前面位置相同,
则填\(m-1\),一层总和就是\((m-1)^{l[i]-1} \cdot m\)。
但是先在多了一个条件相邻两层所选集合不能相同。
2、那么怎么办?很容易想到DP,\(g(i)\)表示前i层满足条件的所有方案数
容易得到一个错误的转移方程\(g(i) = g(i-1)*((m-1)^{l[i]-1} \cdot m) - g(i-1)\)
这个方程的意思是g(i)第i层选了所有能选的方案数,然后再减去g(i-1)即 i和i-1层冲突的方案数
有两个错误
(1)\(i\)和\(i-1\)层冲突的方案数并不是\(g(i-1)\),因为\(i-1\)层与\(i\)层除了一一对应相等,调换顺序也可以,因为只要集合一样即可。
(2)假如说\(l[i-1]>l[i]\)的话那么\(g[i-1]\)包括一些颜色集合大小可能大于第i层的,那么一定会多减去一部分。
根据上面两个错误,我们能反推出一道新题目。
即l[i]满足单调,且相邻层不相等的方案数,就可以用前面的简单递推了。
3、前面说了那么多,无不指向一点——————我们需要多给\(g(i)\)加一维,即用了多少颜色,这要我们就能很方便的处理 2、(1)(2)内的两个问题了
(1)既然有了颜色一维,就可以\(*f[l[i]][j]*(^{m}_{j})*j!\)算出这i与i-1层冲突的方案数。
(\(f[i][j]表示i个位置用了j个颜色的方案数,满足颜色没有标号)\)
(2)以为有了颜色数量这一层,那么我们减去i与i-1层冲突层的数量时候,可以根据i的颜色数量来减去。
这样就很好解决了前两个问题。
4、回到前面的\(f[i][j]\),做这题的时候我才发现一直把这东西搞混了
\(f[i][j] = f[i-1][j-1]+(j-1)f[i-1][j]\)其实求的是颜色无标号的方案。
(\(f[i][j]表示i个位置用了j个颜色的方案数,满足颜色没有标号)\)
为什么呢?假设有编号,首先这样推出的选择序列一定时颜色小的出现在颜色大的前面。
这样的序列就对应无标号方案,假设有标号那么
存在12234,13342这样的相同的情况,总会出现第一个小于另一种情况的方案不会算上。
不同无标号的方案也一定算一个答案。
所有以后题目:有5种颜色,染色1000个地盘,相邻的不能同种颜色,问选其中j种颜色染色方案数。
不能求完\(f[1000][j]*(^{5}_{j})\)就完了还得乘上\(f[1000][j]*j!*(^{5}_{j})\)
点击查看代码
#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;
}

浙公网安备 33010602011771号