1. 程式人生 > 其它 >Codeforces gym 102482 D

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)=i0i!i!xi=1x1。那麼總的分配數目為 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+d1)。這裡為了方便,後面都除去 d ! 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+j1)), 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})
gi,j=k(ki)(gk,jk+min(r,k)fk,jk)

時間複雜度為 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=1d,列舉最終獲得 ≥ 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=1di=0nmin(i,r)(in)[xd]((1xxk)i(1x1xk)ni)=i=0nk=1dmin(i,r)(in)[xd](1x)n(xk)i(1xk)ni=j=0d(i=0nmin(i,r)(in)[xj](xi(1x)ni))(jkd[xdjk](1x)n1)=j=0d(i=0nmin(i,r)(in)[xj](xi(1x)ni))(jkd(djkn+djk1))
前半部分事實上只需分別計算 ∑ 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(1x)ni) ∑ 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(1x)ni)
前者相當於:
[ 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]1u(ux+1x)n=[xjur]1u((u1)x+1)n=(jn)[ur]1u(u1)j=(1)jr(jn)(rj1)
後者也是類似的。
後半部分可以預處理出 ( n + d − i − 1 d − i ) \binom{n+d-i-1}{d-i} (din+di1),然後計算每個數約數處的和即可。用類似埃式篩的做法容易做到 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;
}