1. 程式人生 > >洛谷4260:博弈論與概率統計(組合數學+莫隊/分塊)

洛谷4260:博弈論與概率統計(組合數學+莫隊/分塊)

題面
題意:小L在玩遊戲,贏了n場,輸了m場
贏一場得1分,輸一場扣1分
若當前為0分,則不會扣
問期望得分

前置技能
有一個n個1和m個-1的序列,求字首和最小值≥0的方案數
考慮不合法的
找到第一個和為-1的字首
將其1與-1翻轉
得到一個有n+1個1和m1個-1的序列
恰好與不合法的方案一一對應

類比得字首和最小值恰好為i的方案數為Cn+mn+iCn+mn+i1

考慮n≥m
字首和最小值為i貢獻為n-m+i

經過一輪畫柿子,錯位相減
答案為(nm)Cn+mn

+i=0mCn+mi

f(n+m,m)=i=0mCn+mi

f(i,j)O(1)推出f(i+1,j)f(i,j1)
故可用莫隊或分塊優化

#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
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<N;i++) I[i]=I[p%i]*(p-p/i)%p; for(int i=1;i<N;i++) jc[i]=jc[i-1]*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<l) now=(now+now-C(L,R)+p)%p,L++; while(L>l) now=(now+C(L-1,R))%p*I[2]%p,L--; while(R<r) now=(now+C(L,R+1))%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; }