題目描述
輸入格式
第一行一個整數n(n≤200000)。
接下來n行,每行兩個整數Ai和Bi(Ai,Bi≤3000)。
輸出格式
由於實際答案可能很大,你只需要輸出E*C(n,2)的值對998244353取餘的結果,其中E表示期望值的準確值。
樣例輸入40 01 10 11 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; }