[BJOI2018]治疗之雨
我还没疯
发现如果我们将血量抽象成点,一轮操作抽象成图上的一条边,我们如果能求出每一条边的概率,我们就能搞一下这道题
假设我们求出了这个图\(E\),设\(dp_i\)表示从\(i\)点到达\(0\)点的期望路径长度
那么我们可以列出如下的方程
\]
发现这个方程可以高斯消元来做
问题变成了如何求出这张图
我们如求出了经过\(k\)次减小的操作,血量\(i\)变成血量\(j\)的概率是多少,我们讨论一下那个增加的操作,就能把这张图求出来了
发现\(k\)很大\(n\)较小,于是觉得可以矩阵优化
优化个鬼啊
设\(dp_{i,j}\)表示经过\(i\)次减小操作血量为\(j\)的概率
显然有
\]
发现这个柿子确实可以矩阵优化一下,变成\(O(n^3logk)\)
之后套上消元,就有\(40\)分了
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 205
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const LL mod=1000000007;
inline int read() {
int x=0;char c=getchar();while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
void exgcd(LL a,LL b,LL &x,LL &y) {if(!b) {x=1,y=0;return;}exgcd(b,a%b,y,x);y-=a/b*x;}
inline LL inv(LL t) {LL x,y;exgcd(t,mod,x,y);return (x%mod+mod)%mod;}
int n,p,T;
LL m,k;
LL ans[maxn][maxn],a[maxn][maxn],b[maxn][maxn],E[maxn];
inline void did_ans() {
LL mid[maxn][maxn];
for(re int i=0;i<=n;i++)
for(re int j=0;j<=n;j++) mid[i][j]=ans[i][j],ans[i][j]=0;
for(re int k=0;k<=n;k++)
for(re int i=0;i<=n;i++)
for(re int j=0;j<=n;j++)
ans[i][j]=(ans[i][j]+a[i][k]*mid[k][j]%mod)%mod;
}
inline void did_a() {
LL mid[maxn][maxn];
for(re int i=0;i<=n;i++)
for(re int j=0;j<=n;j++) mid[i][j]=a[i][j],a[i][j]=0;
for(re int k=0;k<=n;k++)
for(re int i=0;i<=n;i++)
for(re int j=0;j<=n;j++)
a[i][j]=(a[i][j]+mid[i][k]*mid[k][j]%mod)%mod;
}
inline void Quick(LL b) {while(b) {if(b&1) did_ans();b>>=1ll;did_a();}}
int main() {
T=read();
while(T--) {
n=read(),p=read(),m=read(),k=read();
memset(b,0,sizeof(b));memset(ans,0,sizeof(ans));
memset(E,0,sizeof(E));memset(a,0,sizeof(a));
if(!p) {puts("0");continue;}
if(!k) {puts("-1");continue;}
if(!m&&k==1) {puts("-1");continue;}
if(!m){
int ans=0;
while(p>0){if(p<n) p++;p-=k;ans++;}
printf("%d\n",ans); continue;
}
LL Inv=inv(m+1ll);
for(re int i=1;i<=n;i++)
a[i][i+1]=Inv,a[i][i]=m*Inv%mod;a[n][n+1]=0;
a[0][0]=1,a[0][1]=Inv;
for(re int i=0;i<=n;i++)
ans[i][i]=1;
Quick(k);
for(re int i=1;i<n;i++) {
for(re int j=1;j<=i;j++)
b[i][j]+=ans[j][i]*Inv%mod*m%mod,b[j][i]%=mod;
for(re int j=1;j<=i+1;j++)
b[i][j]+=ans[j][i+1]*Inv%mod,b[j][i]%=mod;
}
for(re int i=1;i<=n;i++) b[n][i]=ans[i][n];
for(re int i=1;i<=n;i++) b[i][n+1]=mod-1;
for(re int i=1;i<=n;i++) b[i][i]-=1ll,b[i][i]=(b[i][i]+mod)%mod;
for(re int i=1;i<=n;i++) {
LL t=inv(b[i][i]);
for(re int j=n+1;j>=i;--j)
b[i][j]=(b[i][j]*t)%mod;
for(re int j=i+1;j<=n;j++)
for(re int k=n+1;k>=i;--k)
b[j][k]=(b[j][k]-b[j][i]*b[i][k]%mod+mod)%mod;
}
E[n]=b[n][n+1];
for(re int i=n-1;i;--i) {
E[i]=b[i][n+1];
for(re int j=i+1;j<=n;j++)
E[i]=(E[i]-E[j]*b[i][j]%mod+mod)%mod;
}
printf("%lld\n",E[p]);
}
return 0;
}
显然复杂度不对,尤其是矩阵这边
根据高中数学必修三,显然血量减少\(r\)的概率应该是
\]
于是处理一下组合数就不用矩阵了
现在的复杂度就只剩下高斯消元的\(O(n^3)\)了
发现这个矩阵非常特殊,第\(i\)行只有前\(i+1\)列有值,于是我们往下消元的时候一行只需要消三个数就可以了
复杂度\(O(Tn^2)\)
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 1505
#define re register
#define LL long long
const LL mod=1000000007;
inline int read() {
int x=0;char c=getchar();while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int n,p,T;LL m,k;
LL ans[maxn],b[maxn][maxn],E[maxn],C[maxn];
void exgcd(LL a,LL b,LL &x,LL &y) {if(!b) {x=1,y=0;return;}exgcd(b,a%b,y,x);y-=a/b*x;}
inline LL inv(LL t) {LL x,y;exgcd(t,mod,x,y);return (x%mod+mod)%mod;}
inline LL quick(LL a,LL b) {LL S=1;while(b) {if(b&1ll) S=S*a%mod;b>>=1ll;a=a*a%mod;}return S;}
int main() {
T=read();
while(T--) {
n=read(),p=read(),m=read(),k=read();
memset(b,0,sizeof(b));memset(ans,0,sizeof(ans));
memset(E,0,sizeof(E));memset(C,0,sizeof(C));
if(!p) {puts("0");continue;}
if(!k) {puts("-1");continue;}
if(!m&&k==1) {puts("-1");continue;}
if(!m){
int ans=0;
while(p>0){if(p<n) p++;p-=k;ans++;}
printf("%d\n",ans); continue;
}
LL Inv=inv(m+1ll),D=inv(quick(m+1ll,k));
C[0]=1;LL now=k,fac=1,t=k;C[1]=k;
for(re int i=2;i<=n;i++) t--,fac=(fac*i)%mod,now=(now*t)%mod,C[i]=now*inv(fac)%mod;
for(re int i=0;i<=n&&k>=i;i++) ans[i]=C[i]*quick(m,k-i)%mod*D%mod;
for(re int i=1;i<n;i++) {
for(re int j=1;j<=i;j++)
b[i][j]+=ans[i-j]*Inv%mod*m%mod,b[j][i]%=mod;
for(re int j=1;j<=i+1;j++)
b[i][j]+=ans[i+1-j]*Inv%mod,b[j][i]%=mod;
}
for(re int i=1;i<=n;i++) b[n][i]=ans[n-i];
for(re int i=1;i<=n;i++) b[i][n+1]=mod-1;
for(re int i=1;i<=n;i++) b[i][i]-=1ll,b[i][i]=(b[i][i]+mod)%mod;
for(re int i=1;i<=n;i++) {
LL t=inv(b[i][i]);
for(re int j=n+1;j>=i;--j)
b[i][j]=(b[i][j]*t)%mod;
for(re int j=i+1;j<=n;j++) {
b[j][n+1]=(b[j][n+1]-b[j][i]*b[i][n+1]%mod+mod)%mod;
for(re int k=i+1;k>=i;k--)
b[j][k]=(b[j][k]-b[j][i]*b[i][k]%mod+mod)%mod;
}
}
E[n]=b[n][n+1];
for(re int i=n-1;i;--i) {
E[i]=b[i][n+1];
for(re int j=i+1;j<=n;j++)
E[i]=(E[i]-E[j]*b[i][j]%mod+mod)%mod;
}
printf("%lld\n",E[p]);
}
return 0;
}
[BJOI2018]治疗之雨的更多相关文章
- 【BZOJ5292】[BJOI2018]治疗之雨(高斯消元)
[BZOJ5292][BJOI2018]治疗之雨(高斯消元) 题面 BZOJ 洛谷 题解 设\(f[i]\)表示剩余\(i\)点生命时的期望死亡的次数. 考虑打\(k\)次下来脸上被打了\(i\)下的 ...
- [BZOJ5292][BJOI2018]治疗之雨(概率DP+高斯消元)
https://blog.csdn.net/xyz32768/article/details/83217209 不难找到DP方程与辅助DP方程,发现DP方程具有后效性,于是高斯消元即可. 但朴素消元显 ...
- luoguP4457 [BJOI2018]治疗之雨 概率期望 + 高斯消元
应该是最后一道紫色的概率了....然而颜色啥也代表不了.... 首先看懂题意: 你现在有$p$点体力,你的体力上限为$n$ 在一轮中, 1.如果你的体力没有满,你有$\frac{1}{m + 1}$的 ...
- [BZOJ5292] [BJOI2018]治疗之雨
题目链接 BZOJ:https://lydsy.com/JudgeOnline/problem.php?id=5292 洛谷:https://www.luogu.org/problemnew/show ...
- BZOJ5292 & 洛谷4457 & LOJ2513:[BJOI2018]治疗之雨——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=5292 https://www.luogu.org/problemnew/show/P4457 ht ...
- 洛谷P4457/loj#2513 [BJOI2018]治疗之雨(高斯消元+概率期望)
题面 传送门(loj) 传送门(洛谷) 题解 模拟赛的时候只想出了高斯消元然后死活不知道怎么继续--结果正解居然就是高斯消元卡常? 首先有个比较难受的地方是它一个回合可能不止扣一滴血--我们得算出\( ...
- 题解 「BJOI2018 治疗之雨」
题目传送门 题目大意 有一个初始为 \(p\) 的数,每次操作分为以下两个: 有 \(\frac{1}{m+1}\) 的概率$+1,但是中途 \(p\) 的最大值只能为 \(n\)$ 有 \(k\) ...
- 【LOJ】#2513. 「BJOI2018」治疗之雨
题解 具体就是列一个未知数方程\(dp[i]\)表示有\(i\)滴血的时候期望多少轮 \(dp[i] = 1 + \sum_{j = 1}^{i + 1} a_{i,j}dp[j]\) \(dp[n] ...
- 「BJOI2018」治疗之雨
传送门 Description 有\(m+1\)个数,第一个数为\(p\),每轮:选一个数\(+1\),再依次选\(k\)个数\(-1\) 要求如果第一个数\(=N\),不能选它\(+1\),如果第一 ...
随机推荐
- LumiSoft
SVN: https://svn.lumisoft.ee:8443/svn/LumiSoft_Net/ User: readonly Password: readonly Download: http ...
- 实现 如 goole closure 类似功能模块加载函数
看过goole closure 的同学都知道 其中定义一个类名函数时候只要 inlude("") 想加载某个模块只要require("")就可以利用: ...
- Win7 开机启动
1.注册表里面写代码,设置程序以开机启动; 但这样会需要管理员权限,添加程序以管理员权限启动后,又无法直接进入到软件启动界面,UAC控制 代码一: /// <summary> /// 设置 ...
- 六、mybatis分页插件集成
本文基于上一篇“集成mybatis”内容 1.添加依赖 <!-- mybatis-pageHelper --> <dependency> <groupId>com. ...
- Mybatis插入、查询自定义的数据类型的方式
1.首先创建JavaBean对象 package com.zuo.Mybatis.bean; public class PhoneNumber { private String countryCode ...
- Service的启动流程源码跟踪
前言: 当我们在一个Activity里面startService的时候,具体的执行逻辑是怎么样的?需要我们一步步根据源码阅读. 在阅读源码的时候,要关注思路,不要陷在具体的实现细节中,一步步整理代码的 ...
- 三种角度解释href/src/link/import区别
网上查到的几种不同但比较容易理解的解释 解释一: href是Hypertext Reference的缩写,表示超文本引用.用来建立当前元素和文档之间的链接.常用的有:link.a.例如: <li ...
- VC++ 崩溃处理以及打印调用堆栈
title: VC++ 崩溃处理以及打印调用堆栈 tags: [VC++, 结构化异常处理, 崩溃日志记录] date: 2018-08-28 20:59:54 categories: windows ...
- 排序算法lowb三人组-插入排序
def insert_sort(li): for i in range(1, len(li)): # i表示摸到的牌的下标 tmp = li[i] # 摸到的牌 j = i - 1 while j & ...
- 牛顿迭代,多项式求逆,除法,开方,exp,ln,求幂
牛顿迭代 若 \[G(F_0(x))\equiv 0(mod\ x^{2^t})\] 牛顿迭代 \[F(x)\equiv F_0(x)-\frac{G(F_0(x))}{G'(F_0(x))}(mod ...