bzoj1079 [SCOI2008]着色方案

21 篇文章 0 订阅

有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 1000000007

int 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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值