有
n
n
n个
x
x
x和
m
m
m个
y
y
y,每次其中
k
k
k个合并成一个,权值变为它们的平均值,求只剩一个数的权值有多少种可能。
n
,
m
,
k
≤
3000
n,m,k≤3000
n,m,k≤3000
x
,
y
≤
1
0
18
x,y≤10^{18}
x,y≤1018
题解
如果
x
=
y
x=y
x=y,那么答案是
1
1
1,否则显然
x
x
x和
y
y
y无论是多少,答案都只和
n
,
m
,
k
n,m,k
n,m,k有关。
直接令
x
=
0
,
y
=
1
x=0,y=1
x=0,y=1,考虑把初始的
n
+
m
n+m
n+m个数放在一棵
k
k
k叉树上,
0
0
0对最后权值的贡献是
0
0
0,
1
1
1对最后权值的贡献就是
(
1
k
)
d
e
e
p
(frac{1}{k})^{deep}
(k1)deep,其中
d
e
e
p
deep
deep为这个数的深度。
那么最后的数就是
V
=
∑
(
1
k
)
d
e
e
p
V=sum(frac{1}{k})^{deep}
V=∑(k1)deep,可以理解为是一个
k
k
k进制下的小数,
如果没有进位,那这个数小数部分数位之和
S
=
m
S=m
S=m,每进位一次
S
S
S就要减去
k
−
1
k-1
k−1,所以
S
≡
m
(
m
o
d
k
−
1
)
Sequiv mpmod {k-1}
S≡m(modk−1),且
S
≤
m
S≤m
S≤m.
在某种操作方式下,如果把
0
0
0和
1
1
1交换,那么
1
−
V
1-V
1−V表示成的
k
k
k进制数数位之和
S
′
S'
S′就应该满足
S
′
≡
n
(
m
o
d
k
−
1
)
S'equiv npmod {k-1}
S′≡n(modk−1),且
S
≤
n
S≤n
S≤n.
而此时的
S
′
S'
S′是可以通过
S
S
S计算出来的,若这个小数位数为
l
l
l,则
S
′
=
(
l
−
1
)
∗
(
k
−
1
)
+
k
−
S
S'=(l-1)*(k-1)+k-S
S′=(l−1)∗(k−1)+k−S,类似于
1.000
…
1.000…
1.000…减去某个数列竖式减法。
这样就相当于要构造一个
k
k
k进制小数,满足数位和为
S
S
S,
S
S
S满足上面的限制,
S
S
S对应的
S
′
S'
S′也要满足上面的限制,求方案数。
设
f
i
,
j
f_{i,j}
fi,j表示前
i
i
i位和为
j
j
j的方案数,
O
(
n
)
O(n)
O(n)转移,总复杂度
O
(
n
3
)
O(n^3)
O(n3),用前缀和可以优化到
O
(
n
2
)
O(n^2)
O(n2)。
代码
#include<cstdio>#include<cstring>#include<algorithm>usingnamespace std;#define md 1000000007#define ll long long #define N 3010
ll f[N *2][N][2], g[N];intmain(){int n, m, k, i, j; ll x, y;scanf("%d%d%d%lld%lld",&m,&n,&k,&x,&y);
f[0][0][0]=1;for(i =1; i <=(n + m)/(k -1); i++){
g[0]=(f[i -1][0][0]+ f[i -1][0][1])% md;for(j =1; j <= n; j++) g[j]=(g[j -1]+ f[i -1][j][0]+ f[i -1][j][1])% md;for(j =0; j <= n; j++){if(j){if(j - k <0) f[i][j][1]= g[j -1];else f[i][j][1]=(g[j -1]- g[j - k]+ md)% md;}
f[i][j][0]=(f[i -1][j][0]+ f[i -1][j][1])% md;}}
ll ans =0;for(i =0; i <=(n + m)/(k -1); i++){for(j =0; j <= n; j++)if(j %(k -1)== n %(k -1)&& i *(k -1)- j +1<= m)(ans += f[i][j][1])%= md;}if(x == y) ans =1;printf("%lld
", ans);return0;}