题面
🔗
黑板上有 n n n 个 0 0 0 和 m m m 个 1 1 1,我们每次选择 k k k 个数字将其擦除,然后把它们的平均数写上去,这样一直操作直到只剩下一个数字,问剩下的这个数字有多少种不同的情况。
答案对 1 0 9 + 7 10^9+7 109+7 取模。
1 ≤ n , m ≤ 2000 , 2 ≤ k ≤ 2000 , ( k − 1 ) ∣ ( n + m − 1 ) 1\leq n,m\leq2000,2\leq k\leq2000,(k-1)|(n+m-1) 1≤n,m≤2000,2≤k≤2000,(k−1)∣(n+m−1) 。
题解
最终剩下的数字很可能是个分数,但是我们可以把它表示成一个 k k k 进制小数。
如果我们将 k k k 个 1 进行合成,可以浪费掉 k − 1 k-1 k−1 个 1,将 k k k 个 0 进行合成,可以浪费掉 k − 1 k-1 k−1 个 0 。并且,如果将两个小数进行合成,也一定会造成浪费( 2 k 2k 2k 个 0 或 1 组成了原本 k k k 个就可以表示的数)。
所以我们可以求所有 n ′ , m ′ n',m' n′,m′ 个 0 和 1( n ′ = n − a ( k − 1 ) , m ′ = m − b ( k − 1 ) n'=n-a(k-1),m'=m-b(k-1) n′=n−a(k−1),m′=m−b(k−1))不浪费组成的小数个数。不浪费就是指,黑板上有且仅有一个小数(在 ( 0 , 1 ) (0,1) (0,1) 内),每次合并将 k − 1 k-1 k−1 个 0 或 1 与该小数合并,使得小数位右移一位( k k k 进制位),同时填充 1 k \frac{1}{k} k1 位为 0 ∼ k − 1 0\sim k-1 0∼k−1 之间的数。
为了方便转移,我们令
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示总共用去
i
i
i 个 0 和 1,其中有
j
j
j 个
1
1
1 的不浪费结果数,转移如下:
d
p
[
k
]
[
0
∼
k
−
1
]
=
1
d
p
[
i
]
[
j
]
=
∑
d
=
0
k
−
1
d
p
[
i
−
k
+
1
]
[
j
−
d
]
dp[k][0\sim k-1]=1\\ dp[i][j]=\sum_{d=0}^{k-1}dp[i-k+1][j-d]
dp[k][0∼k−1]=1dp[i][j]=d=0∑k−1dp[i−k+1][j−d]
于是很容易用前缀和优化做到 O ( n 2 ) O(n^2) O(n2) 。
CODE
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<random>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
#define MAXN 2005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB long double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
static const int maxn = 1000000;
static char b[maxn];
static int pos = 0,len = 0;
if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
if(pos == len) return -1;
return b[pos ++];
}
//#define getchar() xchar()
LL read() {
LL f = 1,x = 0;int s = getchar();
while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x<0) putchar('-'),x = -x;
return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}
const int MOD = 1000000007;
int n,m,s,o,k;
int dp[MAXN<<1][MAXN];
int sm[MAXN];
int main() {
// freopen("eternalaverage.in","r",stdin);
// freopen("eternalaverage.out","w",stdout);
m = read(); n = read(); k = read();
if(m == 0 || n == 0) return AIput(1,'\n'),0;
for(int i = 1;i < k;i ++) dp[k][i] = 1;
for(int i = k+k-1;i <= n+m;i ++) {
for(int j = 1;j <= n;j ++) sm[j] = (sm[j-1] + dp[i-k+1][j]) % MOD;
for(int j = 1;j <= n && j <= i;j ++) {
dp[i][j] = (sm[j]+MOD-sm[max(0,j-k)]) % MOD;
}
}
int ans = 0;
for(int i = n;i > 0;i -= (k-1)) {
for(int j = m;j >= 0;j -= (k-1)) {
(ans += dp[i+j][i]) %= MOD;
}
}
AIput(ans,'\n');
return 0;
}