Codeforces gym 102482 D
技術標籤:集訓隊作業codeforces生成函式
不妨最後再加上初始時的
r
r
r個寶石。
考慮某個人得到寶石個數的EGF,顯然有
F
(
x
)
=
∑
i
≥
0
i
!
x
i
i
!
=
1
1
−
x
F(x)=\sum_{i\geq 0}\frac{i!x^i}{i!}=\frac{1}{1-x}
F(x)=∑i≥0i!i!xi=1−x1。那麼總的分配數目為
d
!
[
x
d
]
F
n
(
x
)
=
d
!
(
n
+
d
−
1
d
)
d![x^d]F^n(x)=d!\binom{n+d-1}{d}
d![xd]Fn(x)=d!(dn+d−1)。這裡為了方便,後面都除去
d
!
d!
考慮組合意義,相當於每個人得到任意個數的寶石,總共分
d
d
d個(寶石間無序)。這樣,令
f
i
,
j
f_{i,j}
fi,j表示
i
i
i個人分
j
j
j個寶石的方案數(顯然
f
i
,
j
=
(
i
+
j
−
1
j
)
f_{i,j}=\binom{i+j-1}{j}
fi,j=(ji+j−1)),
g
i
,
j
g_{i,j}
gi,j表示所有方案中前
r
r
r大之和,則答案即為
g
n
,
d
f
n
,
d
\frac{g_{n,d}}{f_{n,d}}
fn,dgn,d。那麼,列舉
i
i
i個人中至少有一個寶石的人的個數
k
k
k,有
g
i
,
j
=
∑
k
(
i
k
)
(
g
k
,
j
−
k
+
min
(
r
,
k
)
⋅
f
k
,
j
−
k
)
g_{i,j}=\sum_{k}\binom{i}{k}(g_{k,j-k}+\min(r,k)\cdot f_{k,j-k})
時間複雜度為
O
(
n
2
d
)
\mathcal O(n^2d)
O(n2d)。
#include <bits/stdc++.h>
using namespace std;
typedef long double ldb;
ldb C[1005][1005];
void pre(int n) {
for(int i=0;i<=n;i++) C[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++) C[i][j]=C[ i-1][j-1]+C[i-1][j];
}
ldb f[1005][1005];
int main() {
int n,d,r;
scanf("%d%d%d",&n,&d,&r);
pre(n+d);
f[0][0]=1;
for(int i=1;i<=d;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=i&&k<=j;k++) f[i][j]+=C[j][k]*(f[i-k][k]+min(k,r)*C[i-1][k-1]);
ldb ans=f[d][n]/C[n+d-1][d]+r;
printf("%.10f\n",(double)ans);
return 0;
}
如果答案是在取模意義下(不必考慮精度問題),利用GF,我們可以做的更好。
顯然只需要計算
g
(
n
,
d
)
g(n,d)
g(n,d)。對
k
=
1
∼
d
k=1\sim d
k=1∼d,列舉最終獲得
≥
k
\geq k
≥k個寶石的人數
i
i
i,有:
g
(
n
,
d
)
=
∑
k
=
1
d
∑
i
=
0
n
min
(
i
,
r
)
(
n
i
)
[
x
d
]
(
(
x
k
1
−
x
)
i
(
1
−
x
k
1
−
x
)
n
−
i
)
=
∑
i
=
0
n
∑
k
=
1
d
min
(
i
,
r
)
(
n
i
)
[
x
d
]
(
x
k
)
i
(
1
−
x
k
)
n
−
i
(
1
−
x
)
n
=
∑
j
=
0
d
(
∑
i
=
0
n
min
(
i
,
r
)
(
n
i
)
[
x
j
]
(
x
i
(
1
−
x
)
n
−
i
)
)
(
∑
j
k
≤
d
[
x
d
−
j
k
]
1
(
1
−
x
)
n
)
=
∑
j
=
0
d
(
∑
i
=
0
n
min
(
i
,
r
)
(
n
i
)
[
x
j
]
(
x
i
(
1
−
x
)
n
−
i
)
)
(
∑
j
k
≤
d
(
n
+
d
−
j
k
−
1
d
−
j
k
)
)
\begin{aligned} g(n,d)&=\sum_{k=1}^{d}\sum_{i=0}^{n}\min(i,r)\binom{n}{i}[x^d]((\frac{x^k}{1-x})^i(\frac{1-x^k}{1-x})^{n-i})\\ &=\sum_{i=0}^{n}\sum_{k=1}^{d}\min(i,r)\binom{n}{i} [x^d]\frac{(x^k)^i(1-x^k)^{n-i}}{(1-x)^n}\\ &=\sum_{j=0}^{d}(\sum_{i=0}^{n}\min(i,r)\binom{n}{i}[x^j](x^i(1-x)^{n-i})) (\sum_{jk\leq d}[x^{d-jk}]\frac{1}{(1-x)^n})\\ &=\sum_{j=0}^{d}(\sum_{i=0}^{n}\min(i,r) \binom{n}{i}[x^j](x^i(1-x)^{n-i})) (\sum_{jk\leq d}\binom{n+d-jk-1}{d-jk}) \end{aligned}
g(n,d)=k=1∑di=0∑nmin(i,r)(in)[xd]((1−xxk)i(1−x1−xk)n−i)=i=0∑nk=1∑dmin(i,r)(in)[xd](1−x)n(xk)i(1−xk)n−i=j=0∑d(i=0∑nmin(i,r)(in)[xj](xi(1−x)n−i))(jk≤d∑[xd−jk](1−x)n1)=j=0∑d(i=0∑nmin(i,r)(in)[xj](xi(1−x)n−i))(jk≤d∑(d−jkn+d−jk−1))
前半部分事實上只需分別計算
∑
i
=
0
r
(
n
i
)
[
x
j
]
(
x
i
(
1
−
x
)
n
−
i
)
\sum_{i=0}^{r}\binom{n}{i}[x^j](x^i(1-x)^{n-i})
∑i=0r(in)[xj](xi(1−x)n−i)和
∑
i
=
0
r
i
(
n
i
)
[
x
j
]
(
x
i
(
1
−
x
)
n
−
i
)
\sum_{i=0}^{r}i\binom{n}{i}[x^j](x^i(1-x)^{n-i})
∑i=0ri(in)[xj](xi(1−x)n−i)。
前者相當於:
[
x
j
u
r
]
(
u
x
+
1
−
x
)
n
1
−
u
=
[
x
j
u
r
]
(
(
u
−
1
)
x
+
1
)
n
1
−
u
=
(
n
j
)
[
u
r
]
(
u
−
1
)
j
1
−
u
=
(
−
1
)
j
−
r
(
n
j
)
(
j
−
1
r
)
\begin{aligned} [x^ju^r]\frac{(ux+1-x)^n}{1-u}&=[x^ju^r]\frac{((u-1)x+1)^n}{1-u}\\ &=\binom{n}{j}[u^r]\frac{(u-1)^j}{1-u}\\ &=(-1)^{j-r}\binom{n}{j}\binom{j-1}{r} \end{aligned}
[xjur]1−u(ux+1−x)n=[xjur]1−u((u−1)x+1)n=(jn)[ur]1−u(u−1)j=(−1)j−r(jn)(rj−1)
後者也是類似的。
後半部分可以預處理出
(
n
+
d
−
i
−
1
d
−
i
)
\binom{n+d-i-1}{d-i}
(d−in+d−i−1),然後計算每個數約數處的和即可。用類似埃式篩的做法容易做到
O
(
n
log
log
n
)
\mathcal O(n\log \log n)
O(nloglogn)。
於是總時間複雜度為
O
(
n
log
log
n
)
\mathcal O(n\log \log n)
O(nloglogn)。
#include <bits/stdc++.h>
#define MOD 998244353
using namespace std;
typedef long long ll;
inline void add(int &x,int y) {
((x+=y)>=MOD)?x-=MOD:0;
}
ll pow_mod(ll x,int k) {
ll ans=1;
while (k) {
if (k&1) ans=ans*x%MOD;
x=x*x%MOD;
k>>=1;
}
return ans;
}
int facd[15000005],facv[15000005];
void pre(int n) {
facd[0]=1;
for(int i=1;i<=n;i++) facd[i]=(ll)facd[i-1]*i%MOD;
facv[n]=pow_mod(facd[n],MOD-2);
for(int i=n-1;i>=0;i--) facv[i]=(ll)facv[i+1]*(i+1)%MOD;
}
inline ll C(int n,int m) {
return (n<m)?0:(ll)facd[n]*facv[m]%MOD*facv[n-m]%MOD;
}
bool check[15000005];
int sumv[15000005];
void fwt(int n) {
for(int i=2;i<=n;i++)
if (!check[i]) {
for(int j=n/i;j>0;j--) {
check[i*j]=1;
add(sumv[j],sumv[i*j]);
}
}
}
int main() {
int n,d,r;
scanf("%d%d%d",&n,&d,&r);
pre(max(n,d));
ll s=1;
for(int i=0;i<d;i++) {
sumv[d-i]=s*facv[i]%MOD;
s=s*(n+i)%MOD;
}
fwt(d);
ll ans=0;
for(int i=r+1;i<=n&&i<=d;i++) {
ll v=(C(n-1,i-1)*C(i-2,r-1)%MOD*n-C(n,i)*C(i-1,r)%MOD*r)%MOD;
ans=(ans+(((i-r)&1)?MOD-1:1)*(v+MOD)%MOD*sumv[i])%MOD;
}
ans=(ans+(ll)sumv[1]*n)%MOD;
ans=(ans*pow_mod(s*facv[d]%MOD,MOD-2)+r)%MOD;
printf("%lld\n",ans);
return 0;
}