哇,考试考数学真的是不能玩了,特别还是考这种极其恶心的
题意因为版权原因不能放上来,不过可以告诉你答案要mod 998244353
。。。
题解:(趁我现在还记得,赶紧写下来。。。)
首先说一下期望的概念:
这道题要求的是方案的期望
那么E=sigma(当前方案出现的概率*当前方案的方案数)
概率在这里很显然是1/C(n,2),那么要求最后答案也要乘上C(n,2)
就抵消了,所以实际要求的是方案的总数
来考虑选择第i个盒子和第j个盒子时的方案数
因为总共只有两种小球,那么方案数就等于C(a[i]+a[j]+b[i]+b[j],a[i]+a[j])
即在要放成一排的位置中选出白(红)球要放的位置
于是,直接暴力求解的方案就出来了,枚举所有的方案,求和。
时间复杂度O(n^2),直接爆炸(考试的时候这种垃圾暴力都写错了。。。)
正解是将小球问题转换成网络路径问题
对于一个n*m的网格图,从(0,0)走到(n,m)的方案数是C(n+m,n)
这是可以直接得出来的,但是也可以通过dp的方式得出
dp[i][j]=dp[i][j-1]+dp[i-1][j]
不难看出,这个dp是可以叠加起来做的
举个例子:
在用dp计算(0,0)到(n,m)的方案数时,并不妨碍我们顺便求出
(1,1)到(n-1,m-1)的值,只需要在dp[1][1]++,就可以了
但仍然不能搞,还要引入负的坐标
那么对于这道题读入一个小球时,就在dp[-a[i]][-b[i]]++
那么第i个盒子和第j个盒子所构成的方案数就是
在坐标轴上(-a[i],-b[i])到(a[j],b[j])的dp值
坐标为负的情况加一个3000就可以了
完美解决了需要枚举的问题,巧妙利用了之前求出的值Orz
最后要去重,这种弄在一起搞dp,会出现dp[-a[i]][-b[i]]到dp[a[i]][b[i]]
的情况,要把这种情况减去,还有就是在已经选了i和j后,还会再选一次j和i
这个把最后的答案除以2,也就是乘2的逆元就可以了
这样,就可以做了,还有一些细节见代码
code:
#include
#include
#include
#include
#include
#include
const int MAXC=12000;
const int MAXA=6000;
const int mod=998244353;
typedef long long LL;
using namespace std;
int jc[MAXC+10],rjc[MAXC+10];
int dp[MAXA+10][MAXA+10];
int cnt[3010][3010];
int n,a,b,sum,ans;
inline void prepare(){
jc[0]=jc[1]=rjc[0]=rjc[1]=1;
for(int i=2;i<=MAXC;i++)
{
jc[i]=1ll*jc[i-1]*i%mod;
rjc[i]=1ll*(mod-mod/i)*rjc[mod%i]%mod;
}
for(int i=2;i<=MAXC;i++)
rjc[i]=1ll*rjc[i]*rjc[i-1]%mod;
}
inline int C(int n,int k){
return 1ll*jc[n]*rjc[k]%mod*rjc[n-k]%mod;
}
inline int add(int a,int b){
return a+b>mod?a+b-mod:a+b;
}
inline void Read(int &Ret){
char ch;bool flag=0;
for(;ch=getchar(),!isdigit(ch);)if(ch=='-')flag=1;
for(Ret=ch-'0';ch=getchar(),isdigit(ch);Ret=Ret*10+ch-'0');
flag&&(Ret=-Ret);
}
int main()
{
Read(n); prepare();
for(int i=1;i<=n;i++)
{
Read(a); Read(b);
sum=add(sum,C((a+b)<<1,a<<1));
dp[-a+3000][-b+3000]++;
cnt[a][b]++;
}
for(int i=0;i<=MAXA;i++)
for(int j=0;j<=MAXA;j++)
{
if(i) dp[i][j]=add(dp[i][j],dp[i-1][j]);
if(j) dp[i][j]=add(dp[i][j],dp[i][j-1]);
if(i>=3000&&j>=3000&&cnt[i-3000][j-3000])
ans=add(ans,1ll*dp[i][j]*cnt[i-3000][j-3000]%mod);
}
ans=add(mod,ans-sum);
ans=1ll*ans*rjc[2]%mod;
printf("%d\n",ans);
}