G - BBQ Hard
阿新 • • 發佈:2020-07-20
AGC001E
AT1983
首先我是完全不會,所以學習了別人的小想法。
樸素的演算法是\(O(n^2)\)的,因此一定會炸,所以我們要將\(i\)和\(j\)分離,以求得一個\(O(n)\)或與\(n\)無關的演算法
但是萬惡的組合數讓我們毫無頭緒,這時,你會發現,你迷失在了數字的海洋裡
為了改變現狀,你決定,成為偶像 考慮組合數的組合意義
\(C_{x + y}^{x}\)可以表示為從點\((0,0)\)到\((x,y)\)的走法數量
因此\(C_{a_i+b_i+a_j+b_j}^{a_i+a_j}\)表示為從點\((0,0)\)到\((a_i+a_j,b_i+b_j)\)的走法數量
把原點平移一下,\(C_{a_i+b_i+a_j+b_j}^{a_i+a_j}\)
考慮把所有的\((-a_i,-b_i)\)標記+1,
然後\(dp[i][j] = dp[i - 1][j] + dp[i][j - 1]\)遞推
統計所有\((a_i,b_i)\)的\(dp\)值的和
但是這樣會重複,對於原題的式子\(\sum_{i = 1}^{n} \sum_{j = i + 1}^{n} C_{a_i+a_j+b_i+b_j}^{a_i+a_j}\),我們算了\(i == j\) 時的情況,同時每對\((i,j)\)還算了兩遍
減去即可
#include<bits/stdc++.h> using namespace std; const int N = 2050; const int mod = 1e9 + 7; int n; int a[200050],b[200050]; int dp[N << 1][N << 1]; long long fac[10050]; long long inv[10050]; long long ksm(long long x,int y){ long long z = 1; while(y){ if(y & 1) z = z * x % mod; y >>= 1; x = x * x % mod; } return z; } long long C(int n,int m){ if(n < m || m < 0) return 0; if(n == m || m == 0) return 1; return fac[n] * inv[m] % mod * inv[n - m] % mod; } int main(){ fac[0] = 1; for(int i = 1; i <= 10000; ++ i) fac[i] = fac[i - 1] * i % mod; inv[10000] = ksm(fac[10000],mod - 2); for(int i = 9999; i >= 0; -- i) inv[i] = inv[i + 1] * (i + 1) % mod; scanf("%d",&n); for(int i = 1; i <= n; ++ i) scanf("%d%d",&a[i],&b[i]); for(int i = 1; i <= n; ++ i) dp[2001 - a[i]][2001 - b[i]] += 1; for(int i = 1; i <= 4005; ++ i) for(int j = 1; j <= 4005; ++ j){ dp[i][j] += (dp[i - 1][j] + dp[i][j - 1]) % mod; dp[i][j] %= mod; } long long ans = 0; /*for(int i = 1998; i <= 2003; ++ i) { for(int j = 1999; j <= 2002; ++ j) printf("%d ",dp[i][j]); puts(""); }*/ for(int i = 1; i <= n; ++ i){ //printf("%d\n",dp[2001 + a[i]][2001 + b[i]]); ans = ans + dp[2001 + a[i]][2001 + b[i]], ans %= mod; } //printf("%lld\n",ans); for(int i = 1; i <= n; ++ i){ ans -= C(a[i] + a[i] + b[i] + b[i], a[i] + a[i]); ans = (ans + mod) % mod; } ans = ans * ksm(2, mod - 2) % mod; printf("%lld\n",ans); return 0; }