题意:有n种袜子,给定每种袜子数量ai,每次抽取袜子不放回,求拿到两支同种袜子的期望次数n≤3e5,ai≤3000n\le3e5,a_i\le3000n≤3e5,ai≤3000
Solution: 期望、生成函数、分治
\quad期望E=∑i∗qiE=\sum i * q_iE=∑i∗qi,其中qiq_iqi表示恰好第i次抽取到两只同种袜子的概率,即前i-1次抽取的袜子各不相同,第i次抽到之前出现过的袜子的概率
\quad设pip_ipi,表示前i次抽取的袜子各不相同的概率,考虑p和q关系,可以发现qi=pi−1−piq_i=p_{i-1}-p_iqi=pi−1−pi,因为pi−1p_{i-1}pi−1表示前i-1次抽取结果各不相同,而第i次的抽取可能会出现之前同种袜子或者不出现,pip_{i}pi表示前i次抽取结果各不相同,即前i-1次抽取结果各不相同,第i次抽取出现了之前种类的袜子,可以理解为pip_ipi是包含在pi−1p_{i-1}pi−1中的子事件,相减后即是前i-1次抽取结果各不相同,第i次抽到之前出现过的袜子类型,因此有如上关系式。
\quad于是我们化简式子E=∑i∗qi=∑i(pi−1−pi)=∑i=2n+1(ipi−1−ipi)=2(p1−p2)+3(p2−p3)....+(n+1)(pn−pn+1)=2p1+p2+p3+...+pn+pn+1,其中p1=1,pn+1=0(鸽巢原理)E=\sum i*q_i=\sum i(p_{i-1} - p_i)=\sum_{i=2}^{n+1} (ip_{i-1}-ip_i)=2(p_1-p_2)+3(p_2-p_3)....+(n+1)(p_n-p_{n+1})=2p_1+p_2+p_3+...+p_n+p_{n+1},其中p_1=1, p_{n+1}=0(鸽巢原理)E=∑i∗qi=∑i(pi−1−pi)=∑i=2n+1(ipi−1−ipi)=2(p1−p2)+3(p2−p3)....+(n+1)(pn−pn+1)=2p1+p2+p3+...+pn+pn+1,其中p1=1,pn+1=0(鸽巢原理)
\quad最终转化成求pip_ipi,设tot=∑aitot=\sum a_itot=∑ai,分母为总方案数即CtotiC_{tot}^{i}Ctoti,分子为[xi]∏j=1n(1+ajx)[x^i]\prod_{j=1}^{n}(1+a_jx)[xi]∏j=1n(1+ajx),这里用生成函数意义求解,通俗理解就是每种袜子里取1或者0个即Cak1C_{a_k}^{1}Cak1orCak0C_{a_k}^{0}Cak0,生成函数意义可以解决这个问题。求此式子时候使用分治。复杂度O(nlogn^2)
\quad本题的关键点我认为是考虑到p和q的关系,正难则反
const int N = 2e5+5;
const int mod = 998244353;
const int maxn = 6e6+10;
const int p = 998244353;
int Pow(int x,int d){
int tans = 1;
if(d == 0)return 1%p;
int a = Pow(x,d/2);
tans = 1ll*a*a%p;
if(d%2)tans = 1ll*tans*x%p;
return tans%p;
}
typedef vector<int> Poly;//多项式定义
int F1[maxn],F2[maxn];
int rev[maxn];
void NTT(int * A,int lim,int opt) {
int i, j, k, m, gn, g, tmp;
for(int i = 0; i < lim; ++i)rev[i] = (i & 1)*(lim >> 1) + (rev[i >> 1] >> 1);
for(i = 0; i < lim; ++i)if (rev[i] < i) swap(A[i], A[rev[i]]);
for(m = 2; m <= lim; m <<= 1) {
k = m >> 1;
gn = Pow(3,(p - 1) / m);
for(i = 0; i < lim; i += m) {
g = 1;
for (j = 0; j < k; ++j, g = 1ll * g * gn % p) {
tmp = 1ll * A[i + j + k] * g % p;
A[i + j + k] = (A[i + j] - tmp + p) % p;
A[i + j] = (A[i + j] + tmp) % p;
}
}
}
if(opt == -1){
reverse(A+1,A+lim);
int inv = Pow(lim,p-2);
for(i = 0; i < lim; ++i) A[i] = 1ll * A[i] * inv % p;
}
}
Poly mul(const Poly & A,const Poly & B){
int n = A.size(),m = B.size();
int siz = n + m - 1;
Poly C(siz);
if(siz < 64){//小于等于64项直接暴力算
for(int i = 0;i < n;i++){
for(int j = 0;j < m;j++)C[i+j] = (C[i+j] + 1LL*A[i]*B[j]%p)%p;
}
return C;
}
int fsiz = 1;
while(fsiz <= siz)fsiz <<=1;
for(int i = 0;i < fsiz;i++)F1[i] = F2[i] = 0;
for(int i = 0;i < n;i++)F1[i] = A[i];
for(int i = 0;i < m;i++)F2[i] = B[i];
NTT(F1,fsiz,1);
NTT(F2,fsiz,1);
for(int i = 0;i < fsiz;i++)F1[i] = 1ll*F1[i]*F2[i]%p;
NTT(F1,fsiz,-1);
for(int i = 0;i < siz;i++){
C[i] = F1[i];
}
return C;
}
vector<int> a;
Poly cal(int l ,int r){
if(l == r){
Poly ans = {1, a[l]};
return ans;
}
int mid = (l+r)>>1;
return mul(cal(l,mid),cal(mid+1,r));
}
int n;
signed main(){
cin >> n;
a.resize(n, 0);
for(int &x : a){
cin >> x;
}
int tot = accumulate(a.begin(), a.end(), 0);
auto f = cal(0, n - 1);
int ans = 0, tmp = 1;
for(int i = 0; i <= n; i++){
ans = (ans + f[i] * Pow(tmp, mod - 2) % mod) % mod;
tmp = tmp * (tot - i) % mod;
tmp = tmp * Pow(i + 1, mod - 2) % mod;
}
cout << ans;
}