LOJ6300 BZOJ5283 [CodePlus 2018 3 月赛]博弈论与概率统计
一道好题!很久以前就想做了,咕到了现在,讲第二遍了才做。
首先我们观察到p是没有用的 因为赢的次数一定 那么每一种合法序列出现的概率均为pn∗(1−p)m 是均等的 我们可以不看它了
然后我们可以通过计算所有序列的答案再除以Cnn+m就可以了
然后我们开始进行神奇操作
赢的话就是+1输的话就是-1 那么我们可以观察到最后的结果就是n−m−min(si) 其中s表示前缀和
那么我们有答案就是Cnn+m(n−m)−min(si) 其中第一项直接解决 我们考虑处理第二项
第二项我们用一个f[x]来表示 最小值为x的方案数 这个玩意直接求并不好求 那么我们考虑利用差分性来求
我们再设g[x]表示最小值<=x的方案数 于是f[x]=g[x]−g[x−1]
考虑求数组g
我们发现这个数列的转移只有两种 (x,y)−>(x+1,y+1) 或者 (x,y)−>(x+1,y−1) 分别对应着Alice赢和Bob赢
如果一个序列的最小值<=k那么它一定会有一部分位于y=k上/以下 接下来继续思考
如果我们把第一次接触到y=k的点以后的图像翻转 那么最后一个节点的坐标就是(n+m,2∗k−n+m)
这样的话我们就可以得到比较优美的性质 就是Alice赢了k+m次 Bob赢了n−k次
那么显然这样的折线应该是有Cm+kn+m个 于是可以得到g[x]=Cm+kn+m 推到f[x]=Cm+kn+m−Cm+k−1n+m
继续向下推导
首先发现n>=m 和 n<m是不一样的 因为前者的最小值区间是[−m,0] 后者是[−m,n−m]
这两类是不一样的于是我们分类讨论
对于第一种我们如下推导
ans=(n−m)Cnn+m−x∗f[x]
ans=(n−m)Cnn+m−∑0x=−mCm+xn+m−Cm+x−1n+m
ans=(n−m)Cnn+m+∑mx=0Cm−xn+m−Cm−x−1n+m
ans=(n−m)Cnn+m+∑mx=0x∗Cm−xn+m−∑m−1x=0x∗Cm−x−1n+m
ans=(n−m)Cnn+m+∑m−1x=0Cm−x−1n+m
ans=(n−m)Cnn+m+∑m−1x=0Cxn+m
对于n<m我在这里留给读者自行推导 (才不是因为我懒
最后的柿子也很相似
ans=∑n−1x=0Cxn+m
然后我们就落到了最后一个问题
组合数前缀和怎么算= =
这是一个常见问题
我们有
∑mx=0Cxm+1=∑mx=0(Cxn+Cx−1n)=(2∗∑mx=0Cxn)−Cmn
详细理解请参照杨辉三角
然后呢 我们惊奇的发现 特么多组询问!!!
这个咋整呢
我们可以利用莫队来做 我们对于组合数前缀和可以O(1)转化了 那么 我们就可以愉快的套上莫队美滋滋
在这里我遇到了一点小问题 就是在初始化的地方 如果id循环到N就是可以过的 循环到T是会T的 具体情况我也不知道
感觉和底层优化? 或者计算机硬件有关? 这个如果有神仙知道为什么的话请一定联系我!会超级感谢!
这个题整体思路都很神仙 大概不看的话是完全想不到的 用莫队来做组合数前缀和很久以前也就听说过了 在这里第一次使用到
还有此题略微卡常 虽然我没遇到 但是前面那个小问题还是卡了很久 看了标程对着才改到 所以这个题还是可以多看看学习一下的
最后的复杂度是O((n+m)√(n+m))
不清楚为什么大家块大小都取的477 跟风一波。
代码扔这里了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//Love and Freedom. #include<cstring> #include<cmath> #include<algorithm> #include<cstdio> #define ll long long #define inf 20021225 #define N 250001 #define B 477 #define mdn 1000000007 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();} while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int ksm(int bs,int mi) { int ans=1; while(mi) { if(mi&1) ans=1ll*ans*bs%mdn; bs=1ll*bs*bs%mdn; mi>>=1; } return ans; } struct node { int l,r,id; node(){} node(int _l,int _r,int _id){l=_l,r=_r,id=_id;} }k[N],a[N]; int id[N],fac[N],inv[N],ans,fin[N],T; void add(int &x,int y){x=x+y>=mdn?x+y-mdn:x+y;} void sub(int &x,int y){x=x-y<0?x-y+mdn:x-y;} bool cmp(node a,node b) { if(id[a.l]==id[b.l]) return a.r<b.r; return id[a.l]<id[b.l]; } int C(int n,int m) { if(n<m) return 0; return 1ll*fac[n]*inv[m]%mdn*inv[n-m]%mdn; } int invC(int n,int m) { if(n<m) return 0; return 1ll*inv[n]*fac[m]%mdn*fac[n-m]%mdn; } void upd(int n,int m,int f) { if(f==1) add(ans,ans),sub(ans,C(n,m)); else if(f==2) add(ans,C(n-1,m)),ans=1ll*ans*inv[2]%mdn; else if(f==3) add(ans,C(n,m+1)); else sub(ans,C(n,m)); } void solve() { for(int i=1;i<N;i++) id[i]=i/B+1; sort(k+1,k+T+1,cmp); int n=0,m=0; ans=1; for(int i=1;i<=T;i++) { while(n<k[i].l) upd(n++,m,1); while(n>k[i].l) upd(n--,m,2); while(m<k[i].r) upd(n,m++,3); while(m>k[i].r) upd(n,m--,4); add(fin[k[i].id],ans); } } int main() { T=read(); int p=read(); fac[0]=1; for(int i=1;i<N;i++) fac[i]=1ll*fac[i-1]*i%mdn; inv[N-1]=ksm(fac[N-1],mdn-2); for(int i=N-1;i;i--) inv[i-1]=1ll*inv[i]*i%mdn; for(int i=1;i<=T;i++) { int n=read(),m=read(); a[i].l=n,a[i].r=m; if(n>=m) fin[i]=1ll*(n-m)*C(n+m,n)%mdn,k[i]=node(n+m,m-1,i); else k[i]=node(n+m,n-1,i); } solve(); for(int i=1;i<=T;i++) { int tmp=1ll*fin[i]*invC(a[i].l+a[i].r,a[i].r)%mdn; printf("%d\n",tmp); } return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 超详细,DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方Dee
· 用 DeepSeek 给对象做个网站,她一定感动坏了
· .NET 8.0 + Linux 香橙派,实现高效的 IoT 数据采集与控制解决方案
· DeepSeek处理自有业务的案例:让AI给你写一份小众编辑器(EverEdit)的语法着色文件