Live2D

题解 「BJOI2018 治疗之雨」

题目传送门

题目大意

有一个初始为 \(p\) 的数,每次操作分为以下两个:

  • \(\frac{1}{m+1}\) 的概率$+1,但是中途 \(p\) 的最大值只能为 \(n\)$

  • \(k\) 次减少操作,每次有 \(\frac{1}{m+1}\) 的概率 \(-1\)

(每次先操作操作 \(1\) 然后操作操作 \(2\)

\(p\) 变为 \(0\) 的期望操作次数。

\(T\) 次询问,每次保证 \(1\le p\le n\le 1500,m,k\le 10^9\),答案对 \(10^9+7\) 取模。

思路

其实这道题很水,也不知道怎么评到黑题的。

首先,我们可以发现,\(k\) 次被打中 \(i\) 次的概率为 \(\frac{\binom{k}{i}m^{k-i}}{(m+1)^k}\) ,也就是说我们可以算出一次操作由 \(i\) 变为 \(j\) 的概率,我们假设这个为 \(p_{i,j}\)

然后我们发现肯定可以设 \(f_i\) 表示当前值为 \(i\) 的剩余期望操作次数,我们可以得到转移式:

\[f_i=\sum_{j=0}^{i+1}p_{i,j}\times f_j+1 \]

\[\Rightarrow f_{i+1}=\frac{1}{p_{i,i+1}}(f_i-1-\sum_{j=0}^{i}p_{i,j}\times f_j) \]

然后你发现这个东西可以用一个套路做了,就是说我们发现每一个 \(f_i\) 我们都可以表示为 \(a\times f_1+b\) 的形式,然后我们递推一下解个方程就可以求出 \(f_1\) 继而求出答案了。

时间复杂度 \(\Theta(Tn\log n)\)

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define mod 1000000007
#define MAXN 1505

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
int qkpow (int a,int b){
	int res = 1;for (;b;b >>= 1,a = mul (a,a)) if (b & 1) res = mul (res,a);
	return res;
}
int inv (int x){return qkpow (x,mod - 2);}

int n,m,k,st,invx,invm,ci[MAXN],binom[MAXN];

int calc (int x){//计算k轮中减少x的概率 
	if (x < 0 || x > k) return 0;
	else return mul (binom[x],mul (ci[x],invx));
}

int pro (int i,int j){
	if (i == n) return calc (i - j); 
	else return add (mul (calc (i - j),dec (1,invm)),mul (calc (i - j + 1),invm));
}

struct node{
	int k,b;//表示为k*f[1]+b
	node (){}
	node (int _k,int _b){k = _k,b = _b;}
	node operator + (int x){return node (k,add (b,x));} 
	node operator - (int x){return node (k,dec (b,x));}
	node operator * (int x){return node (mul (k,x),mul (b,x));}
	node operator + (node x){return node (add (k,x.k),add (b,x.b));}
	node operator - (node x){return node (dec (k,x.k),dec (b,x.b));}
}f[MAXN];

signed main(){
	int T;read (T);
	while (T --> 0){
		read (n,st,m,k),invx = inv (qkpow (m + 1,k)),invm = inv (m + 1);
		if (!k){puts ("-1");continue;}
		if (!m){
			if (k == 1) puts ("-1");
			else{
				int cnt = 0;
				while (st > 0) cnt ++,st = min (n,st + 1) - k;
				write (cnt),putchar ('\n');
			}
			continue;
		}
		binom[0] = 1;for (Int i = 1;i <= n;++ i) binom[i] = mul (binom[i - 1],mul (k - i + 1,inv (i)));
		for (Int i = 0;i <= n && i <= k;++ i) ci[i] = qkpow (m,k - i);
		f[1] = node (1,0);
		for (Int i = 1;i <= n - 1;++ i){
			f[i + 1] = node (0,0);
			for (Int j = 1;j <= i;++ j) f[i + 1] = f[i + 1] + f[j] * pro (i,j);
			f[i + 1] = (f[i] - 1 - f[i + 1]) * inv (pro (i,i + 1));
		}
		node sum = node (0,0);
		for (Int i = 1;i <= n;++ i) sum = sum + f[i] * pro (n,i);
		sum = f[n] - 1 - sum;int f1 = mul (mod - sum.b,inv (sum.k));
		write (add (f[st].b,mul (f1,f[st].k))),putchar ('\n');
	}
	return 0;
}
posted @ 2020-09-09 13:35  Dark_Romance  阅读(118)  评论(0编辑  收藏  举报