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