[BZOJ3391]小球游戏

题目描述


输入格式

第一行一个整数n(n≤200000)。

接下来n行,每行两个整数Ai和Bi(Ai,Bi≤3000)。


输出格式

由于实际答案可能很大,你只需要输出E*C(n,2)的值对998244353取余的结果,其中E表示期望值的准确值。


样例输入
4
0 0
1 1
0 1

1 0


样例输出

12


样例解释:
取一二盒子:{br},{rb} 2种;
取一三盒子:{r} 1种;
取一四盒子:{b} 1种;
取二三盒子:{brr},{rbr} ,{rrb} 3种;
取二四盒子:{bbr},{brb},{rbb} 3 种;
取三四盒子:{br} ,{rb} 2种;

共12种。


来源 by azui



题解,来自azui:
E*C(n,2)的值就是总情况数。
转换思路,将小球模型转化为网格路径模型来看待。
一共有a个红球,b个蓝球,放在一排的方案数等于a行b列的方格沿着右、下两个方向从左上角走到右下角的方案数。
如果是两个盒子 i 和 j,那就是坐标系上从(-ai,-bi)走到(aj,bj)的方案数(最少步数)。
将每个盒子拆成(ai,bi)和(-ai,-bi)两个点。现在考虑从第三象限中任意一点走到第一象限中任意一点的方案数之和(记为 X)。X 的求法显然,只需一个6000*6000的网格dp即可做到。
其与答案的出入为:增加了 j≤i 的非法情况。
我们可以O(n)减去i和j相等的情况,就只剩下j<i的非法情况了,显然由于对称性,只需除以2即可(乘以 2 的逆元)。


#include<cstdio>
const int Mod=998244353;
const int T=3000;
const int N=6000;
const int M=200000;

void Getin( int &shu ) {//最好加上读入优化, 以及别开long long, 改用1ll
	char c; int f=1; shu=0;
	for( c=getchar(); c<'0' || c>'9'; c=getchar() ) if( c=='-' ) f=-1;
	for( ; c>='0' && c<='9'; c=getchar() ) shu=shu*10+c-'0';
	shu*=f;
}

int fac[M+5], inv[M+5];
void Pre() {
	fac[0]=fac[1]=inv[0]=inv[1]=1;
	for( int i=2; i<=M; i++ ) {
		fac[i]=1ll*i*fac[i-1]%Mod;
		inv[i]=1ll*( Mod-Mod/i )*inv[Mod%i]%Mod;
		//线性求逆元, 可参考http://blog.miskcoo.com/2014/09/linear-find-all-invert
	}
	for( int i=2; i<=M; i++ )
		inv[i]=1ll*inv[i]*inv[i-1]%Mod;
	//阶乘的逆元等于逆元的阶乘: inv(4!)==inv(1)*inv(2)*inv(3)*inv(4)
}

int C( int m, int r ) { return 1ll*fac[m]*inv[r]%Mod*inv[m-r]%Mod; }
int n, sum, ans, a, b;
int dp[N+5][N+5], cnt[T+5][T+5];

int main() {
	Pre();
	Getin(n);
	for( int i=1; i<=n; i++ ) {
		Getin(a); Getin(b);
		sum=( sum+C( a+a+b+b, a+a ) )%Mod;//记录i==j的情况
		dp[T-a][T-b]++;//将原点(0,0)平移至(3000,3000), (-3000,-3000)变为(0,0)
		cnt[a][b]++;//记重
	}
	
	for( int i=0; i<=N; i++ )
		for( int j=0; j<=N; j++ ) {
			if(i) dp[i][j]=( dp[i][j]+dp[i-1][j] )%Mod;
			if(j) dp[i][j]=( dp[i][j]+dp[i][j-1] )%Mod;
			if( i>=T && j>=T && cnt[i-T][j-T] )
				ans=( ans+1ll*dp[i][j]*cnt[i-T][j-T]%Mod )%Mod;
		}
	
	ans=( ans-sum+Mod)%Mod;
	ans=1ll*ans*inv[2]%Mod;
	printf( "%d\n", ans );
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值