bzoj1079 [SCOI2008]着色方案

来源:互联网 发布:supreme淘宝正品店 编辑:程序博客网 时间:2024/05/30 23:40

有n个木块排成一行,从左到右依次编号为1~n。你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块。

所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n。相邻两个木块涂相同色显得很难看,所以你希望统计任意两

个相邻木块颜色不同的着色方案。

100%的数据满足:1 <= k <= 15, 1 <= ci <= 5


题解

莫种意义上也算是一道dp好题吧,虽然说不难,但是转移很有意思。

注意到直接状压是5 ^ 15的必炸,而15 ^ 5比较靠谱。

考虑如果两个颜色可以涂的次数相等,那么它们是等价的。所以f[x1][x2][x3][x4][x5][k]表示可涂i次的颜色的数量为xi,上一个涂的颜色是k。其余细节见代码注释。

#include<set>#include<cstdio>#include<vector>#include<cstring>#include<iostream>#include<algorithm>using namespace std;inline int read() {int x = 0, flag = 1; char ch = getchar();while (ch > '9' || ch < '0') { if (ch == '-') flag = -1; ch = getchar(); }while (ch <= '9' && ch >= '0') { x = x * 10 + ch - '0'; ch = getchar(); }return x * flag;}#define rep(ii, aa, bb) for (int ii = aa; ii <= bb; ii++)#define drp(ii, aa, bb) for (int ii = aa; ii >= bb; ii--)#define N 1000001#define ll long long#define ha 1000000007int n, a[6];ll f[16][16][16][16][16][6];int vis[16][16][16][16][16][6];ll dp(int x1, int x2, int x3, int x4, int x5, int k) {ll &ans = f[x1][x2][x3][x4][x5][k];if (vis[x1][x2][x3][x4][x5][k]) return ans;if ((x1 | x2 | x3 | x4 | x5) == 0) return 1;if (x1) ans += (x1 - (k == 2)) * dp(x1 - 1, x2, x3, x4, x5, 1); //考虑枚举这次选哪种剩余次数的颜色,一共有多少个剩余这种次数的个数就有多少种选择if (x2) ans += (x2 - (k == 3)) * dp(x1 + 1, x2 - 1, x3, x4, x5, 2); //如果上次填的是颜色剩余次数为2的,意味着颜色中剩余次数为1的多了一个,那么这一次并不能再选这种颜色,这次可以选填1的就要少1。if (x3) ans += (x3 - (k == 4)) * dp(x1, x2 + 1, x3 - 1, x4, x5, 3);if (x4) ans += (x4 - (k == 5)) * dp(x1, x2, x3 + 1, x4 - 1, x5, 4);if (x5) ans += x5 * dp(x1, x2, x3, x4 + 1, x5 - 1, 5);vis[x1][x2][x3][x4][x5][k] = 1;return ans %= ha;}int main() {n = read();rep(i, 1, n) a[read()]++;cout << dp(a[1], a[2], a[3], a[4], a[5], 0);return 0;}


原创粉丝点击