题面
题目描述:
一个杠杆上的左右两侧各有 块质量相等的橡皮(杠杆最中间也有一块),随机从中拿走 块橡皮,可使杠杆依然平衡。求共有多少种拿走橡皮的方案数,答案对 取模。
题目简译:
从 中共选出 个互不相同的数,求使得正数与负数的和为零的方案数。
思路
前置知识:整数划分。
只考虑在杠杆的右侧选出 个数,题目就变成了经典的整数划分模型:
将 划分为 个互不相同且不大于 的正整数的方案数。
显然统计答案时,右侧选出 个数,那么左侧就选出 个数,乘法原理计算得到总方案数。
对于数字有大小限制的整数划分:
令 表示把 划分为 个数的方案:
则有 。
当 时,出现的不合法方案需要减掉:
此时 。
对于统计答案:
如上:枚举杠杆一侧选出 个数,另一侧就选出 个数,乘法原理计算。
特别注意:杠杆最中间也有一块橡皮,需要考虑是否将其拿走。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<climits> #include<cstdlib> #include<vector> using namespace std; typedef long long ll; template <typename T> inline void read(T&x){ int w=0;x=0; char ch = getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') w=1; ch = getchar(); } while(ch>='0'&&ch<='9'){ x = (x<<1)+(x<<3)+(ch^48); ch = getchar(); } if(w) x=-x; } template <typename T,typename...Args> inline void read(T&t,Args&...args){ read(t);read(args...); } const int N = 1e4+10,K = 15; int n,k,p; ll dp[N*K][K]; int main(){ int T;read(T); while(T--){ read(n,k,p); memset(dp,0,sizeof(dp)); dp[0][0] = 1; for(int i=0;i<=k*n;++i){ for(int j=1;j<=min(i,k);++j){ dp[i][j] = (dp[i][j]+dp[i-j][j]+dp[i-j][j-1])%p; if(i>n) dp[i][j] = (dp[i][j]-dp[i-n-1][j-1]+p)%p; } } ll ans = 0; for(int j=0;j<=k;++j){ for(int i=0;i<=n*k;++i){ ans = (ans+dp[i][j]*dp[i][k-j])%p; if(j<k) ans = (ans+dp[i][j]*dp[i][k-j-1])%p; } } printf("%lld\n",ans); } return 0; }
|