CSP 模拟19

A:建设城市(city)

答案就是至少有0个不满足条件的方案数
减去至少1个的
加上至少2个的
以此类推
就可以直接做了
考虑0个的怎么求
m个队 分成n组 方案数就是C(m-1,n-1);
不会的建议找数学老师谢罪

有i个不满足条件的方案数可以用挡板法
假定此时有i个不满足条件
那就挑出ik个 令其固定 不参与剩下的分配
然后在其他的分配好之后 将这i
k个给到i个城市
那么这个城市一定不满足
所以有i个不满足条件的方案数就求出来了





B:军训队列

同一个身高的人是等效的
所以去重之后直接dp就可以了
dp[i][j] 表示前i个人 分成j组 最小的答案
(n^2*k)转移就可以了





C:山屋惊魂

大模拟





D:彩球问题

发现并不关心具体是哪个球
如果当前的球状态是 1 3 2 3
而它和1 2 3 3 是等价的
故我们只关心cnt为1 cnt为2 cnt为3的球有多少个
因此就可以记搜了
dp[i][j][k][l] 表示 当前有1个的球有多少种 有2个的球有多少种 有3个的球有多少种 上次放的是哪种球
然后就可以简单转移了

#include<bits/stdc++.h>
using namespace std;
const int maxn = 14;
const int mod = 1e8;

struct ll {
	long long x[5];
	ll(){memset(x,0,sizeof(x));}
	friend void operator += (ll &A,ll B) {
		for(int i = 0;i < 5;++i) A.x[i] += B.x[i];
		for(int i = 0;i < 5;++i) A.x[i+1] += A.x[i] / mod,A.x[i] %= mod;
	}
	friend ll operator * (ll A,int B) {
		for(int i = 0;i < 5;++i) A.x[i] *= B;
		for(int i = 0;i < 5;++i) A.x[i+1] += A.x[i] / mod,A.x[i] %= mod;
		return A;
	}
	friend bool operator != (ll &A,int B) { 
		return A.x[0] != B;
	}
	void print(int i = 4){
		for(;~i;--i) if(x[i]) {printf("%lld",x[i]);break;}
		for(--i;~i;--i) printf("%08lld",x[i]);
	}
};

ll dp[maxn][maxn][maxn][5];
int a[maxn];
int cnt[maxn];

ll dfs(int cnt1,int cnt2,int cnt3,int pre){
	if(dp[cnt1][cnt2][cnt3][pre] != 0) return dp[cnt1][cnt2][cnt3][pre];
	if(!cnt1 && !cnt2 && !cnt3) {
		dp[cnt1][cnt2][cnt3][pre].x[0] = 1LL;
		return dp[cnt1][cnt2][cnt3][pre];
	}
	ll ans;
	if(cnt1) ans += (dfs(cnt1-1,cnt2,cnt3,0) * (cnt1-(pre==1)));
	if(cnt2) ans += dfs(cnt1+1,cnt2-1,cnt3,1) * (cnt2-(pre==2));
	if(cnt3) ans += dfs(cnt1,cnt2+1,cnt3-1,2) * (cnt3-(pre==3));
	return dp[cnt1][cnt2][cnt3][pre] = ans;
}

int main(){
	freopen("color.in","r",stdin);
	freopen("color.out","w",stdout);
	int n;scanf("%d",&n);
	for(int i = 1;i <= n;++i) scanf("%d",&a[i]),cnt[a[i]]++;
	dfs(cnt[1],cnt[2],cnt[3],0).print();
	return 0;
}

如初见 与初见
原文地址:https://www.cnblogs.com/HISKrrr/p/13849210.html