题意
给定一列球,编号 1 … n 1\dots n 1…n,每一步随机选择一个区间 [ l , r ] [l,r] [l,r],染黑 [ l , r ] [l,r] [l,r] 间的所有球。问染黑至少 m m m 个球所用的期望步数。 n ≤ 100 n\leq 100 n≤100
题解
(Min-max 容斥没学好)
首先给每个球赋值其首次被染色时的步数,要求的即 ( k = n − m + 1 ) th max (k=n-m+1)\th \max (k=n−m+1)thmax 的期望。(类似于 HDU4624 Endless Spin)。
然后 k th min − max k\th \min-\max kthmin−max 反演可得: k th max ( S ) = ∑ T ⊆ S f ∣ T ∣ min ( T ) k\th \max(S) = \sum_{T \subseteq S} f_{|T|} \min(T) kthmax(S)=∑T⊆Sf∣T∣min(T), f i = ( − 1 ) i − k ( i − 1 k − 1 ) f_i = (-1)^{i - k} {i - 1 \choose k - 1} fi=(−1)i−k(k−1i−1)。
设 DP 数组 d p i , j , k dp_{i,j,k} dpi,j,k 为 DP 到第 i i i 个球处(并且选了 i i i),集合 T T T which【有 j j j 个区间不会与 T T T 有交集且 ∣ T ∣ = k |T|=k ∣T∣=k】的个数。转移时枚举下一个选的球。
复杂度瓶颈在于 DP, O ( n 5 ) O(n^5) O(n5)。
代码:
#include<bits/stdc++.h>
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
const int N=110,mod=998244353;
//#define DEC //{{黑幕|define 了之后还得改一下代码}}
#ifdef DEC
#define tp double
tp getinv(tp x){
return 1/x;
}
tp qpow(int x,int y){
return y&1?-1:1;
}
#else
struct tp{
int v;
tp(int x=0):v(x){}
//operator int(){return v;}
};
tp operator+ (tp x,tp y){return (x.v+y.v)%mod;}
bool operator==(tp x,tp y){return x.v==y.v;}
tp operator- (tp x,tp y){return (x.v+mod-y.v)%mod;}
tp operator* (tp x,tp y){return (x.v*1ll*y.v)%mod;}
tp qpow(tp x,int y){
if(x==-1)return y&1?mod-1:1;
tp ans=1;
while(y){
if(y&1)ans=ans*x;
x=x*x;
y>>=1;
}
return ans;
}
tp getinv(tp x){
return qpow(x,mod-2);
}
#endif
inline void up(tp &x,tp y){
x=(x+y);
}
tp dp[N][N*N/2][N];
tp f[N];//f: 容斥系数
int _(int x){
return x*(x-1)/2;
}
tp c[N][N];
int main(){
int n=getint(),m=getint();
m=n-m+1;
c[1][1]=1;
for(int i=2;i<=n+1;i++){
for(int j=1;j<=i;j++)
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
for(int i=0;i<=n;i++){
for(int j=0;j<=i;j++)
c[i][j]=c[i+1][j+1];
}
for(int i=1;i<=n;i++)
f[i]=qpow(-1,i-m)*c[i-1][m-1];
dp[0][0][0]=1;
for(int i=0;i<n;i++){
int limj=_(i);
for(int j=0;j<=limj;j++){
for(int k=0;k<=i;k++){
//cerr<<"? "<<i<<" "<<j<<" "<<k<<" "<<dp[i][j][k]<<endl;
if(dp[i][j][k]==0)continue;
for(int l=i+1;l<=n;l++){
up(dp[l][j+_(l-i)][k+1],dp[i][j][k]);
}
}
}
}
for(int i=0;i<n;i++){
int limj=_(i);
for(int j=0;j<=limj;j++){
for(int k=0;k<=i;k++){
up(dp[n][j+_(n+1-i)][k],dp[i][j][k]);
}
}
}
tp ans=0;
tp limi=_(n+1);
for(int i=0;i<=limi.v;i++){
for(int j=m;j<=n;j++){
if(!dp[n][i][j].v)continue;
ans=ans+dp[n][i][j]*f[j]*(limi*getinv(limi-i));
//cerr<<i<<" "<<j<<" "<<dp[n][i][j]<<" "<<ans<<endl;
}
}
cout<<ans.v<<endl;
return 0;
}