题面
题意:小L在玩游戏,赢了n场,输了m场
赢一场得1分,输一场扣1分
若当前为0分,则不会扣
问期望得分
前置技能
有一个n个1和m个-1的序列,求前缀和最小值≥0的方案数
考虑不合法的
找到第一个和为-1的前缀
将其1与-1翻转
得到一个有 n+1 n + 1 个1和 m−1 m − 1 个-1的序列
恰好与不合法的方案一一对应
类比得前缀和最小值恰好为 −i − i 的方案数为 Cn+in+m−Cn+i−1n+m C n + m n + i − C n + m n + i − 1
考虑n≥m
前缀和最小值为i贡献为n-m+i
经过一轮画柿子,错位相减
答案为 (n−m)∗Cnn+m+∑mi=0Cin+m ( n − m ) ∗ C n + m n + ∑ i = 0 m C n + m i
设 f(n+m,m)=∑mi=0Cin+m f ( n + m , m ) = ∑ i = 0 m C n + m i
由 f(i,j) f ( i , j ) 可 O(1) O ( 1 ) 推出 f(i+1,j) f ( i + 1 , j ) 和 f(i,j−1) f ( i , j − 1 )
故可用莫队或分块优化
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))
typedef long long LL;
const LL p=1e9+7;
const int N=600300,nn=550;
int T,Violet;
LL jc[N],Ijc[N],I[N],ans[N],now=1;
int n[N],m[N];
int L=1,R;
struct yy
{
int l,r,num;
}f[N];
bool cmp(yy x,yy y)
{
if((x.l/nn)==(y.l/nn))
return x.r<y.r;
return (x.l/nn)<(y.l/nn);
}
LL C(int x,int y)
{
return jc[x]*Ijc[y]%p*Ijc[x-y]%p;
}
LL IC(int x,int y)
{
return Ijc[x]*jc[y]%p*jc[x-y]%p;
}
int main()
{
I[1]=jc[0]=Ijc[0]=1;
for(int i=2;i%i]*(p-p/i)%p;
for(int i=1;i1]*i%p,Ijc[i]=Ijc[i-1]*I[i]%p;
cin>>T>>Violet;
for(int i=1;i<=T;i++)
{
f[i].num=i;
scanf("%d%d",&n[i],&m[i]);
f[i].l=n[i]+m[i];
if(n[i]<m[i])
f[i].r=n[i]-1;
else
f[i].r=m[i]-1;
}
sort(f+1,f+T+1,cmp);
for(int i=1;i<=T;i++)
{
int l=f[i].l,r=f[i].r;
while(L%p,L++;
while(L>l)
now=(now+C(L-1,R))%p*I[2]%p,L--;
while(R1))%p,R++;
while(R>r)
now=(now-C(L,R)+p)%p,R--;
ans[f[i].num]=now;
}
for(int i=1;i<=T;i++)
if(n[i]<m[i])
printf("%lld\n",ans[i]*IC(n[i]+m[i],n[i])%p);
else
printf("%lld\n",(n[i]-m[i]+ans[i]*IC(n[i]+m[i],n[i])%p)%p);
return 0;
}