3 条题解
-
0
更好的阅读体验
如果你打得开的话题意
个小球, 种颜色,第 种颜色有 个,保证 ,问有多少种染色方案,使得任意两个相邻小球颜色不同。
题解
这道题有多种方法,都具有一定的启发性,故在这里一并给出。
算法一:暴力 dp
从左到右依次确定每个小球的颜色,直接在状态中表示每个颜色剩余的数量与最后一个小球的颜色,直接转移即可。
时间复杂度 ,无法接受。
算法二:优化状态设计
发现 的范围较小,且剩余个数相同的小球对我们来说没有区别,于是在状态中只需记录每种剩余数量的颜色有多少个,与最后一个小球的颜色的剩余数量即可,转移同样比较显然,在此不再赘述。
时间复杂度 ,使用记忆化搜索实现,可以通过此题。
算法三:改变枚举方式
与 此题 类似(另外吐槽一下,这题咋是黄题啊,本菜鸡觉得挺难的啊……),考虑改变枚举方式,枚举颜色而非枚举小球,并且认为是不断地向小球序列中插入一种新颜色的小球。这样带来的好处是单次插入的小球颜色全部相同,方便统计插入后有多少相邻的同颜色小球。
于是设 表示枚举到第 个颜色,当前小球序列中有 对相邻的同颜色小球的方案数,则答案为 。设 。
考虑在 的基础上插入第 种颜色的小球,设这 个小球被原来的序列分成了 段,且其中 段隔开了原来相邻的一对同颜色小球,则有 。且插入这些小球后,相邻同颜色小球的个数变为 。
再考虑统计方案数,先插板法分段,再选出这 段和 段,方案数为 $\dbinom{c_i-1}{a-1}\times\dbinom{s_{i-1}+1-j}{a-b}\times\dbinom{j}{b}$。
于是可以写出转移方程式:
$$f_{i,j-b+c_i-a}=\sum_{j=0}^{s_{i-1}-1}\sum_{a=1}^{\min(s_{i-1}+1,c_i)}\sum_{b=0}^{\min(a,j)}f_{i-1,j}\times\binom{c_i-1}{a-1}\times\binom{s_{i-1}+1-j}{a-b}\times\binom{j}{b}$$时间复杂度 。
算法四:更加优秀的容斥做法
设 表示钦定 个球与左边的球同色的方案数, 表示恰好 个球与左边的球同色的方案数,则答案为 。根据二项式反演,有 。
现在考虑如何求出 。
设颜色 ,颜色 ,……,颜色 分别有 个球与左边的球同色()。考虑颜色 的小球,在序列中被分成了 段连续的子段,共有 种分段方法。再考虑整个序列,发现就是这 个颜色段的可重排列问题(每种颜色内部的顺序不能改变,因为在枚举分段方法时已经枚举到了)。
于是有:
$$f_k=\sum_{k_1+k_2+\cdots+k_m=k}\binom{c_1-1}{k_1}\binom{c_2-1}{k_2}\cdots\binom{c_m-1}{k_m}\frac{(n-k)!}{(c_1-k_1)!(c_2-k_2)!\cdots(c_m-k_m)!}$$发现这个式子其实是卷积的形式,使用暴力卷积(其实就是背包)可以以 的复杂度求出 。
算法五:使用
fft优化再推一推上面这个式子,答案其实就是这个:
$$\sum_{k=0}^n(-1)^k(n-k)![x^k]\prod_{i=1}^m\sum_{j=0}^{c_i-1}\binom{c_i-1}{j}\frac{x^j}{(c_i-j)!}$$使用
fft求多项式卷积,时间复杂度 。其实我不会写fft
代码
算法一、算法二略。
算法三:
#include <bits/stdc++.h> using namespace std; #define int long long int read() { int ret = 0, f = 1; char ch = getchar(); while (!isdigit(ch)) f = (ch == '-') ? -f : f, ch = getchar(); while (isdigit(ch)) ret = ret * 10 + ch - '0', ch = getchar(); return ret * f; } const int P = 1e9 + 7, N = 1000; int n, col[N], sum[N], f[N][N], c[N][N]; signed main() { n = read(); for (int i = 1; i <= n; ++i) col[i] = read(), sum[i] = sum[i - 1] + col[i]; c[0][0] = 1; for (int i = 1; i <= 100; ++i) { c[i][0] = 1; for (int j = 1; j <= 100; ++j) c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % P; } f[1][col[1] - 1] = 1; for (int i = 1; i < n; ++i) for (int j = 0; j <= sum[i] - 1; ++j) for (int a = 1; a <= min(sum[i] + 1, col[i + 1]); ++a) for (int b = 0; b <= min(j, a); ++b) f[i + 1][j + col[i + 1] - a - b] += f[i][j] * c[sum[i] + 1 - j][a - b] % P * c[j][b] % P * c[col[i + 1] - 1][a - 1] % P, f[i + 1][j + col[i + 1] - a - b] %= P; printf("%lld\n", f[n][0]); return 0; }算法四:
#include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; typedef long long ll; const int MAXN = 105, MOD = 1e9 + 7; inline void ADD(int& x, int y) { x += y; if (x >= MOD) x -= MOD; } inline void DEC(int& x, int y) { x -= y; if (x < 0) x += MOD; } ll q_pow(ll a, ll b, ll p = MOD) { ll ret = 1; for (; b; a = a * a % p, b >>= 1) if (b & 1) ret = ret * a % p; return ret; } ll q_inv(ll x, ll p = MOD) { return q_pow(x, p - 2, p); } int N, M, ans, c[MAXN], f[MAXN], g[MAXN], fac[MAXN], ifac[MAXN]; inline int C(int n, int m) { return 1ll * fac[n] * ifac[m] % MOD * ifac[n - m] % MOD; } int main() { scanf("%d", &M); for (int i = 1; i <= M; ++i) scanf("%d", &c[i]), N += c[i]; fac[0] = 1; for (int i = 1; i <= N; ++i) fac[i] = 1ll * fac[i - 1] * i % MOD; ifac[N] = q_inv(fac[N]); for (int i = N - 1; i >= 0; --i) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % MOD; f[0] = 1; for (int i = 1; i <= M; ++i) { for (int j = 0; j <= N; ++j) g[j] = 0; for (int j = 0; j <= N; ++j) if (f[j]) { for (int k = 0; k < c[i]; ++k) ADD(g[j + k], 1ll * f[j] * C(c[i] - 1, k) % MOD * ifac[c[i] - k] % MOD); } for (int j = 0; j <= N; ++j) f[j] = g[j]; } for (int i = 0; i <= N; ++i) { int tmp = 1ll * fac[N - i] * f[i] % MOD; if (i & 1) DEC(ans, tmp); else ADD(ans, tmp); } printf("%d", ans); return 0; }算法五:不会写 fft,以后再说……
感谢神仙 Mr_Wu 对本篇题解做出的巨大贡献!!!
求点赞 qwq
打个广告:我的博客
-
0
-
0
//std by liangjiewen //不会写动规,只能打记搜 #include<bits/stdc++.h> using namespace std; const long long N=100,mod=1e9+7; long long c[N],dp[20][20][20][20][20][5],a[N];int k; long long dfs(int last,int a,int _b,int c,int d,int e){ if(a+_b+c+d+e==0) return 1; if(dp[a][_b][c][d][e][last]!=-1) return dp[a][_b][c][d][e][last]; long long res=0; if(a) res=((a-(last==2))*dfs(1,a-1,_b,c,d,e)+res)%mod; if(_b) res=((_b-(last==3))*dfs(2,a+1,_b-1,c,d,e)+res)%mod; if(c) res=((c-(last==4))*dfs(3,a,_b+1,c-1,d,e)+res)%mod; if(d) res=((d-(last==5))*dfs(4,a,_b,c+1,d-1,e)+res)%mod; if(e) res=(e*dfs(5,a,_b,c,d+1,e-1)+res)%mod; dp[a][_b][c][d][e][last]=res%mod; return res%mod; } int use[N]; int main(){ memset(dp,-1,sizeof(dp)); scanf("%d",&k); for(int i=1;i<=k;i++) scanf("%lld",&c[i]),use[c[i]]++; long long ans=dfs(0,use[1],use[2],use[3],use[4],use[5]); printf("%lld",ans); }
- 1
信息
- ID
- 2732
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 20
- 已通过
- 13
- 上传者