计数题
trick
-
对于要求两个边的和相等问题,可以将一边设为正数,另外一边设为负数,那么两边相等问题就变为两边之和是否等于 0 问题,在通过调整将值域变为权正,然后等于某个确定了的数。这道题就运用了这样的思想。
-
有一个很基础的计数问题:\(\forall i\in[1,n],a_i\ge 0\) 要求 \(\sum_{i=1}^n x_i = m\),这个就可以用插板法做,计算 \(m+n-1\choose n-1\)。
那我如果升级一下,变成 \(0\le a_i\le h\),那么就是在其基础上添加一个容斥,枚举有多少个数比 \(m\) 写成式子就是: -
如果要算 \(S>T\) 的方案数,如果 \(S>T\) 和 \(S<T\) 之间存在比较直接的关系,比如 \(S>T\) 的方案数与 \(S<T\) 的方案数是相同的,我们可以先算出 \(S=T\),再拿总方案数减去后除以 2。
题目
XMS2C. 概率(pr)
题目大意
有一个随机生成器,随机生成一个长度为 \(2\times n\),值域为 \([0,m]\) 的正整数序列,要求前 \(n\) 个数比后 \(n\) 个数大的概率。
考场思路(70pts)
考场思路
并没有想到正解那样的转化。 我们发现 $n$ 个数的和有 $nm+1$ 种可能值,我们算出每个可能值的方案数,做一遍前缀和,那么前 $n$ 个数比后 $n$ 个数大的方案数就是 $\sum_{i=1}^{nm} f_i\times sum_{i-1}$。算方案数用 DP,状态数 \(O(nm)\),转移 \(O(n)\) 次,单次转移 \(O(m)\),可以用前缀和优化到 \(O(1)\),所以复杂度就是 \(O(n^2m)\),理论上很难过 70pts 的点。
但是我们发现,相同的 \(m\),不同的 \(n\) 其实可以离线下来放在一起算,这样就可以减少很多计算,就可以卡过 70pts。
code
int calc(int n,int m,int now){
sum[0]=f[0];
for(int i=1;i<=now;i++) sum[i]=sum[i-1],add(sum[i],f[i]);
int p=0,q=0;
for(int i=1;i<=now;i++) add(p,1ll*f[i]*sum[i-1]%mod);
q=qpow(m+1,2*n);
return 1ll*p*qpow(q,mod-2)%mod;
}
void solve(int m){
int ppp=0;
int now=0;
f[0]=1;
for(int i=1;;i++){
g[0]=f[0];
now=i*m;
for(int j=1;j<=now;j++) g[j]=g[j-1],add(g[j],f[j]);
for(int j=0;j<=now;j++) f[j]=0,add(f[j], g[j]-(j-m-1<0 ? 0:g[j-m-1])+mod);
while(ppp<q[m].size() && q[m][ppp].first==i) res[q[m][ppp].second]=calc(i,m,now),ppp++;
if(ppp>=q[m].size()) return clean(i,m),void();
}
}
解题思路
解题思路
一个常见的转化:由于前 \(n\) 个和后 \(n\) 个其实是本质相同的,于是我们其实可以统计两个和相等的方案数,再拿总方案数减去后除以 2,就是前面大于后面的方案数。
现在的问题就转化为统计前 \(n\) 个和后 \(n\) 个相等的方案数,下面依然是非常经典的转化:
把后 \(n\) 个数的值域变成 \([-m,0]\),前 \(n\) 个数不变,那么问题又转化为这 \(2n\) 个数的和为 0,继续调整后 \(m\) 个数的值域为 \([0,m]\),那么就变成了统计 \(2n\) 个数的和为 \(nm\),这其实就变成了一个经典组合计数问题:
如果没有 \(a_i\le m\) 的限制,那么就是插板法,多了 \(a_i\le m\) 的限制,那么就多加一个容斥就行了。复杂度 \(O(Tn)\)。
code
int C(int n,int m){
if(n<m) return 0;
if(n==m || m==0) return 1;
return frac[n]*inv[m]%mod*inv[n-m]%mod;
}
int qry(int n,int m){
return C(n+m-1,m-1);
}
void solve(){
cin>>n>>m;
int nm=n*m,ans=0;
int eq=0,p=0,q=qpow(m+1,2*n);
for(int i=0;i*(m+1)<=n*m;i++){
int op=i&1?-1:1;
int now=qry(n*m-i*(m+1), 2*n)*C(2*n,i)%mod;
eq=(eq+op*now+mod)%mod;
}
p=(q-eq+mod)%mod*inv[2]%mod;
cout<<p*qpow(q,mod-2)%mod<<"\n";
}
启发:
-
对于要求两个边的和相等问题,可以将一边设为正数,另外一边设为负数,那么两边相等问题就变为两边之和是否等于 0 问题,在通过调整将值域变为权正,然后等于某个确定了的数。这道题就运用了这样的思想。
-
有一个很基础的计数问题:\(\forall i\in[1,n],a_i\ge 0\) 要求 \(\sum_{i=1}^n x_i = m\),这个就可以用插板法做,计算 \(m+n-1\choose n-1\)。
那我如果升级一下,变成 \(0\le a_i\le h\),那么就是在其基础上添加一个容斥,枚举有多少个数比 \(m\) 写成式子就是:
- 如果要算 \(S>T\) 的方案数,如果 \(S>T\) 和 \(S<T\) 之间存在比较直接的关系,比如 \(S>T\) 的方案数与 \(S<T\) 的方案数是相同的,我们可以先算出 \(S=T\),再拿总方案数减去后除以 2。

浙公网安备 33010602011771号