世界杯正如火如荼地开展!度度熊来到了一家酒吧。
有 N 对情侣相约一起看世界杯,荧幕前正好有 2×N 个横排的位置。
所有人都会随机坐在某个位置上。
当然,如果某一对情侣正好挨着坐,他们就会有说不完的话,影响世界杯的观看。
一般地,对于一个就座方案,如果正好有 K 对情侣正好是挨着坐的,就会产生 DK 的喧闹值。
度度熊想知道随机就座方案的期望喧闹值。
为了避免输出实数,设答案为 ans,请输出 ans×(2N)! mod P 的值。其中 P=998244353
思路:dp[i][j]表示已有i对情侣入座,j对情侣坐在一起的方案数。
for(int i=1;i<=N;i++)
for(int j=0;j<=i;j++)
{
///第i对情侣不分开
dp[i][j]+=dp[i-1][j]*j;///坐在了之前挨着坐的情侣的中间
dp[i][j]+=dp[i-1][j-1]*(2*(i-1)-(j-1)+1);///把之前的(j-1)对情侣看成一团,放在他们的间隙插空坐
///第i对情侣分开
dp[i][j]+=dp[i-1][j+2]*((j+2)*(j+1)/2);///两个人分别拆散一对情侣
dp[i][j]+=dp[i-1][j+1]*(2*(i-1)-(j+1)+1)*(j+1);///其中一个人拆散了一对情侣
dp[i][j]+=dp[i-1][j]*((2*(i-1)-j+1)*(2*(i-1)-j)/2);///两个人都没对其他情侣动手
}
完整代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int N=1000;
ll dp[N+5][N+5],p[N+5];
void init()
{
p[0]=1ll;
for(int i=1;i<=N;i++)
p[i]=p[i-1]*2ll%mod;///情侣之间也有座位先后顺序,预处理出2^n
dp[0][0]=1;
for(ll i=1;i<=N;i++)
for(ll j=0;j<=i;j++)
{
///第i对情侣不分开
dp[i][j]+=dp[i-1][j]*j%mod;
if(dp[i][j]>mod)
dp[i][j]-=mod;
if(j>0)
{
dp[i][j]+=dp[i-1][j-1]*(2*(i-1)-(j-1)+1)%mod;
if(dp[i][j]>mod)
dp[i][j]-=mod;
}
///第i对情侣分开
dp[i][j]+=dp[i-1][j+2]*((j+2)*(j+1)/2)%mod;
if(dp[i][j]>mod)
dp[i][j]-=mod;
dp[i][j]+=dp[i-1][j+1]*(2*(i-1)-(j+1)+1)*(j+1)%mod;
if(dp[i][j]>mod)
dp[i][j]-=mod;
dp[i][j]+=dp[i-1][j]*((2*(i-1)-j+1)*(2*(i-1)-j)/2)%mod;
if(dp[i][j]>mod)
dp[i][j]-=mod;
}
}
int n;
ll d;
int main()
{
init();
while(~scanf("%d%lld",&n,&d))
{
ll ans=0,tmp=1ll;
for(int i=0;i<=n;i++) ///O(n)累加算结果
{
ans+=dp[n][i]*tmp%mod;
if(ans>mod) ans-=mod;
tmp=tmp*d%mod;
}
ans=ans*p[n]%mod;
printf("%lld\n",ans);
}
return 0;
}