题意:
有
n
n
n组物品,每组有
a
i
a_i
ai个肉和
b
i
b_i
bi个菜,你可以选择两组物品让后将肉和菜其串在一根串上,问有多少种不同的串法。
两种方法不同当且仅当选的物品不同或者串的顺序存在至少一个位置一个方法串的肉,另一个串的菜。
n
≤
2
e
5
,
a
i
,
b
i
≤
2
e
3
n\le2e5,a_i,b_i\le2e3
n≤2e5,ai,bi≤2e3
思路:
一个比较巧妙的建模题。
考虑暴力做法,显然答案为
∑
i
=
1
n
∑
j
=
i
+
1
n
(
a
i
+
b
i
+
a
j
+
b
j
a
i
+
a
j
)
\sum_{i=1}^{n}\sum_{j=i+1}^n\binom{a_i+b_i+a_j+b_j}{a_i+a_j}
∑i=1n∑j=i+1n(ai+ajai+bi+aj+bj),但是这个是
n
2
n^2
n2的,而且基本没什么能优化的地方。
看到
a
i
,
b
i
a_i,b_i
ai,bi很小,可以考虑从这里入手。
枚举
a
a
a?也是行不通的。。
还是观察一下式子吧,将其抽象一下,考虑到平面两个点
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
(x_1,y_1),(x_2,y_2)
(x1,y1),(x2,y2),从第一个点到第二个点的路径方案数为
(
x
2
−
x
1
+
y
2
−
y
1
x
2
−
x
1
)
\binom{x_2-x_1+y_2-y_1}{x_2-x_1}
(x2−x1x2−x1+y2−y1)。
嗯?!这两个式子是不是像极了!
我们只需要将其中一个
(
a
i
,
b
i
)
(a_i,b_i)
(ai,bi)变成负数就行了!
所以我们要求的东西变成什么了?
不就是从
∑
j
=
i
+
1
n
p
a
t
h
(
(
−
a
j
,
−
b
j
)
,
(
a
i
,
b
i
)
)
\sum_{j=i+1}^npath((-a_j,-b_j),(a_i,b_i))
∑j=i+1npath((−aj,−bj),(ai,bi))的方案数嘛。
这个好算啊,直接转换成二维平面的
d
p
dp
dp,让后
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
f
[
i
]
[
j
−
1
]
f[i][j]=f[i-1][j]+f[i][j-1]
f[i][j]=f[i−1][j]+f[i][j−1]
但是你会发现一个问题,这个是求的
∑
j
=
1
n
p
a
t
h
(
(
−
a
j
,
−
b
j
)
,
(
a
i
,
b
i
)
)
\sum_{j=1}^npath((-a_j,-b_j),(a_i,b_i))
∑j=1npath((−aj,−bj),(ai,bi)),但是你要求
∑
j
=
i
+
1
n
p
a
t
h
(
(
−
a
j
,
−
b
j
)
,
(
a
i
,
b
i
)
)
\sum_{j=i+1}^npath((-a_j,-b_j),(a_i,b_i))
∑j=i+1npath((−aj,−bj),(ai,bi)),显然我们简单容斥一下,将原来的式子
∑
i
=
1
n
∑
j
=
i
+
1
n
(
a
i
+
b
i
+
a
j
+
b
j
a
i
+
a
j
)
=
(
∑
i
=
1
n
∑
j
=
1
n
(
a
i
+
b
i
+
a
j
+
b
j
a
i
+
a
j
)
−
∑
i
=
1
n
(
2
∗
(
a
i
+
b
i
)
2
∗
a
i
)
)
2
\sum_{i=1}^{n}\sum_{j=i+1}^n\binom{a_i+b_i+a_j+b_j}{a_i+a_j}=\frac{(\sum_{i=1}^n\sum_{j=1}^n\binom{a_i+b_i+a_j+b_j}{a_i+a_j}-\sum_{i=1}^n\binom{2*(a_i+b_i)}{2*a_i})}{2}
∑i=1n∑j=i+1n(ai+ajai+bi+aj+bj)=2(∑i=1n∑j=1n(ai+ajai+bi+aj+bj)−∑i=1n(2∗ai2∗(ai+bi)))
这样问题就解决啦。
//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<random>
#include<cassert>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;
//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int N=2000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;
int n;
int a[N],b[N];
LL f[4010][4010];
LL inv[N],fun[N];
LL C(int a,int b)
{
if(a<0||b<0||a<b) return 0;
return fun[a]*inv[b]%mod*inv[a-b]%mod;
}
LL qmi(LL a,LL b)
{
LL ans=1%mod;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans%mod;
}
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
inv[1]=1;
fun[0]=fun[1]=1;
for(int i=2;i<N;i++)
fun[i]=fun[i-1]*i%mod;
inv[N-1]=qmi(fun[N-1],mod-2);
for(int i=N-2;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]),f[2001-a[i]][2001-b[i]]++;
for(int i=1;i<=4005;i++) {
for(int j=1;j<=4005;j++) {
(f[i][j]+=f[i-1][j])%=mod;
(f[i][j]+=f[i][j-1])%=mod;
}
}
LL ans=0;
for(int i=1;i<=n;i++) {
ans+=f[2001+a[i]][2001+b[i]]; ans%=mod;
ans-=C(2*(a[i]+b[i]),2*a[i]); ans%=mod;
ans+=mod; ans%=mod;
}
cout<<ans*qmi(2,mod-2)%mod<<endl;
return 0;
}
/*
*/