题目链接
https://atcoder.jp/contests/agc009/tasks/agc009_e
题解
又被劝退了。。。
第一步转化非常显然: 就等价于一开始有一个数(1), 有(frac{n+m-1}{k-1})次机会每次选择一个数把它变成(k)个原来的(frac{1}{k}), 最后从(n+m)个数中选出(m)个,问能选出多少不同的数。
然后考虑化成(k)进制小数,假设最后形成的数是(d_1,d_2,...,d_{n+m}), 则(sum^{n+m}_{i=1} d_i=1).
一个(d)进制小数可以被表示成(m)个(k)的负整数次幂之和当且仅当其每一位数值之和不超过(m)且和(m)模((k-1))同余。(显然)
但同时还要保证(1)可以被表示成((n+m))个(k)的负整数次幂之和,且包含这(m)个数。那么就可以转化成(1)减这个小数可以被表示成(n)个(k)的负整数次幂之和。(行吧我就这一步没想出来……自闭了啊……)
所以最后也就是要计算有多少个序列(a_1,a_2,...,a_l (1le lle frac{n+m-1}{k-1})), 满足(0le a_ile k-1, a_l>0, sum^l_{i=1}a_ile m,sum^l_{i=1}a_iequiv m(mod k-1), sum^l_{i=1}k-1-a_ile n-1, sum^l_{i=1}k-1-a_iequiv n-1(mod k-1)), 直接dp即可。时间复杂度(O((n+m)k)).
代码
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cassert>
#define llong long long
using namespace std;
const int N = 4e3;
const int P = 1e9+7;
llong dp[N+3][N+3],sdp[N+3][N+3];
int n1,n2,m,len;
llong ans;
int main()
{
scanf("%d%d%d",&n1,&n2,&m); len = (n1+n2-1)/(m-1);
dp[0][0] = 1ll; for(int j=0; j<=n2; j++) sdp[0][j] = 1;
for(int i=1; i<=len; i++)
{
for(int j=0; j<=n2; j++)
{
if(j>=m) {dp[i][j] = (sdp[i-1][j]-sdp[i-1][j-m]+P)%P;}
else {dp[i][j] = sdp[i-1][j];}
if((n2-j)%(m-1)==0 && i*(m-1)-j<=n1-1 && (n1-1-i*(m-1)+j)%(m-1)==0) {ans = (ans+dp[i][j]-dp[i-1][j]+P)%P;}
}
sdp[i][0] = dp[i][0]; for(int j=1; j<=n2; j++) sdp[i][j] = (sdp[i][j-1]+dp[i][j])%P;
}
printf("%lld
",ans);
return 0;
}