问题描述
样例输入
3 500
1 1
2 3
4 4
样例输出
500000004
200000002
728571435
提示
首先,容易得到题目中的p是没有用的,因为Alice每一种输赢序列的出现概率是相等的,因此只需要算出每种排列的得分之和再除以 Cnn+m C n + m n 即可。
然后将赢记作1,输记作-1,那么得到一个序列 ai a i ,对其求前缀和得到 bi b i
观察可以发现,最终得分等于 ∑ai−min(bi) ∑ a i − m i n ( b i ) ,即 n−m−min(bi) n − m − m i n ( b i )
那么最终答案等于 (n−m)Cnn+m−∑min(bi) ( n − m ) C n + m n − ∑ m i n ( b i )
考虑求后面的东西,令 F[k] F [ k ] 表示 bi b i 的最小值恰为 k k 的排列数,令 G[k] G [ k ] 表示 bi b i 的最小值小于等于 k k 的排列数
考虑利用折线法来求得 G[k] G [ k ]
注意到最终的 bn+m b n + m 一定是 n−m n − m ,那么我们可以认为 ai=1 a i = 1 表示从 (x,y) ( x , y ) 走到 (x+1,y+1) ( x + 1 , y + 1 ) ,而 ai=−1 a i = − 1 表示从 (x,y) ( x , y ) 走到 (x+1,y−1) ( x + 1 , y − 1 ) ,那么 ai a i 序列就是从 (0,0) ( 0 , 0 ) 走到 (n+m,n−m) ( n + m , n − m ) 的一条折线。
考虑 bi b i 的最小值小于等于 k k 的意义,不妨令恰好取得最小值时,前面有 x x 个 1 1 , y y 个 −1 − 1 ,那么有 x−y<=k x − y <= k ,反映到折线上就是这条折线至少有一个点的纵坐标小于等于 k k ,也就是折线与 y=k y = k 有交。
不妨将折线与 y=k y = k 第一次相交后的部分沿 y=k y = k 对称,对称前后的折线一一对应,那么对称后的折线终点是 (n+m,2k−n+m) ( n + m , 2 k − n + m ) ,那么此时对应的 ai a i 中有 k+m k + m 个1, n−k n − k 个-1,且这样的折线有 Cm+kn+m C n + m m + k 条,对应的 ai a i 序列也就有这么多个。
因此我们得到 G[k]=Cm+kn+m G [ k ] = C n + m m + k ,进一步的 F[k]=G[k]−G[k−1]=Cm+kn+m−Cm+k−1n+m F [ k ] = G [ k ] − G [ k − 1 ] = C n + m m + k − C n + m m + k − 1
为了得到答案,我们考虑 min(bi) m i n ( b i ) 的取值范围
容易得到 n>=m时,min(bi)∈[−m,0] n >= m 时 , m i n ( b i ) ∈ [ − m , 0 ] ,当 n<m时,min(bi)∈[−m,n−m] n < m 时 , m i n ( b i ) ∈ [ − m , n − m ] ,因此需要分类讨论
先考虑 n>=m n >= m 时,得到 Ans=(n−m)Cnn+m−∑0k=−mk(Cm+kn+m−Cm+k−1n+m) A n s = ( n − m ) C n + m n − ∑ k = − m 0 k ( C n + m m + k − C n + m m + k − 1 ) ,推导一番
我们发现同样是一个组合数前缀和的形式,那么接下来考虑如何求组合数前缀和。注意到
因此我们可以用莫队算法来处理多次询问。总时间复杂度 O((N+M)N+M−−−−−−√) O ( ( N + M ) N + M )
代码:
#include
#include
#include
#include
#include
#define N 250005
using namespace std;
const int mod=1e9+7,inv2=1000000008>>1;
int id[N],T,p,fac[N],inv[N],Ans;
struct node{int id,l,r;}K[N],A[N];
bool operator<(node a,node b)
{
if(id[a.l]==id[b.l])return a.rreturn id[a.l]int &x,int y){x+=y;x-=x>=mod?mod:0;}
void sub(int &x,int y){x-=y;x+=x<0?mod:0;}
int mul(int x,int y){return 1ll*x*y%mod;}
int C(int n,int m)
{
if(n<m)return 0;
return mul(fac[n],mul(inv[m],inv[n-m]));
}
int inv_C(int n,int m)
{
return mul(inv[n],mul(fac[m],fac[n-m]));
}
void UD1(int n,int m)
{
Ans=mul(Ans,2);
sub(Ans,C(n,m));
}
void UD2(int n,int m)
{
add(Ans,C(n-1,m));
Ans=mul(Ans,inv2);
}
void UD3(int n,int m)
{
add(Ans,C(n,m+1));
}
void UD4(int n,int m)
{
sub(Ans,C(n,m));
}
void Solve()
{
int i,j,k,n,m,S=477;
for(i=1;isort(K+1,K+T+1);
n=0;m=0;Ans=1;
for(i=1;i<=T;i++)
{
while(nm);
while(n>K[i].l)UD2(n--,m);
while(mm++);
while(m>K[i].r)UD4(n,m--);
add(A[K[i].id].id,Ans);
}
}
int main()
{
int i,j,k,n,m;
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(i=2;i1],i);
inv[i]=mul(inv[mod%i],mod-mod/i);
}
for(i=2;i1]);
scanf("%d%d",&T,&p);
for(i=1;i<=T;i++)
{
scanf("%d%d",&n,&m);
A[i].l=n;A[i].r=m;
if(n>=m)A[i].id=mul(n-m,C(n+m,n)),K[i]=(node){i,n+m,m-1};
else K[i]=(node){i,n+m,n-1};
}
Solve();
for(i=1;i<=T;i++)
{
A[i].id=mul(A[i].id,inv_C(A[i].l+A[i].r,A[i].l));
printf("%d\n",A[i].id);
}
}