bzoj3612平衡

题意:

给定有\(2*n+1\)个刻度的尺子,再中间位置有一个支撑点,这样就成了一个跷跷板,每个刻度位置放一个砝码,所以跷跷板平衡。问拿走\(k\)个砝码仍然平衡的方案数。
\(T\)组数据,答案对\(p\)取模
$T <= 20,1 <= n <= 10000,1 <= k <= 10,2 <= p <= 10000,且 k <= 2n+1。 $


动态规划
明显取走砝码和空尺子上放砝码是一样的。
刚开始想到的是
f[i][j][k]:表示前i个点,放j个砝码,得到k力矩的方案数。
f[i][j][k]=f[i-1][j][k]+f[i-1][j-1][k-i]
利用背包同样的方式可以把第一维去掉,节省空间。但是时间复杂度没什么影响,超时。

然后,换个角度想,跷跷板的一侧放i个砝码,产生j的力矩。由于每个砝码等重,实际上就是把i个砝码的距离相加,注意距离都不相等。那么也就是把i个不等的正整数想买得到j的方案数。还有一个重要的条件,每个距离都不大于n。
所以,f[i][j]表示把i分成j个不同的正整数,且每个正整数不超过n的方案数。
f[i][j]=(f[i-j][j]+f[i-j][j-1]-((i>=n+1)?f[i-n-1][j-1]:0))%p;


#include<bits/stdc++.h>
using namespace std;
const int maxn=10010;
int t,n,p,k; 
int f[maxn*11][12];

int main()
{
	freopen("3612.in","r",stdin);
	freopen("3612.out","w",stdout); 
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d",&n,&k,&p);
		f[0][0]=1;
		for(int j=1;j<=k;++j)
			for(int i=j;i<=n*k;++i)
				f[i][j]=(f[i-j][j]+f[i-j][j-1]-((i>=n+1)?f[i-n-1][j-1]:0))%p;
				
		long long ans=0;
		for(int j=0;j<=k;++j)
			for(int i=j;i<=n*k;++i)
			ans=(ans+f[i][j]*(f[i][k-j]+f[i][k-j-1]))%p;
		printf("%lld\n",ans);
	}
		
	return 0;
}

posted on 2022-10-03 15:41  gryzy  阅读(22)  评论(0编辑  收藏  举报

导航