JZOJ6340 B

题目大意

给定一个长度为 N N N的数列 a a a
每次操作在 [ 1.. N ] [1..N] [1..N]中等概率选择一个 a i > 0 a_i>0 ai>0 a i a_i ai,并将其-1.
问期望多少次操作后 a 1 a_1 a1变为 0 0 0.
N , a i < = 5 e 5 N,a_i<=5e5 N,ai<=5e5

Solution

b i b_i bi表示对于 a i a_i ai, a i a_i ai被减了 b i b_i bi次.
根据期望的线性性.
E ( Σ b i ) = Σ E b i E(\Sigma b_i)=\Sigma Eb_i E(Σbi)=ΣEbi.
所以每个 a i a_i ai可以分开讨论.

考虑某一个位置 i i i,假设当前有 c c c个元素不为 0 0 0,那么每个元素被操作的概率都是 1 c \frac{1}{c} c1。倘若只关注 1 1 1 i i i两个元素,可以发现操作其它元素的时候对它们没有影响,而且它们两个被操作的概率是相等的。于是这个问题就等价于一个只有两个元素的原问题。
摘自官方题解.

于是现在就只剩下两个元素.
可以看成一个从 ( a 1 , a i ) (a_1,a_i) (a1,ai)走到 ( 0 , 0 ) (0,0) (0,0)的随机游走,每次选一个坐标-1.
由于 a 1 a_1 a1的贡献是固定的,因为 a 1 a_1 a1必须减为 0 0 0.
所以我们计算答案时先不考虑 a 1 a_1 a1.
那么直接写出答案的式子.
Σ i = 0 a i − 1 i ∗ C a i + i − 1 i 2 a 1 + i + a i ∗ ( 1 − Σ i = 0 a i − 1 C a i + i − 1 i 2 a 1 + i ) \Sigma_{i=0}^{a_i-1}i*\frac{C_{a_i+i-1}^i}{2^{a_1+i}}+a_i*(1-\Sigma_{i=0}^{a_i-1}\frac{C_{a_i+i-1}^i}{2^{a_1+i}}) Σi=0ai1i2a1+iCai+i1i+ai(1Σi=0ai12a1+iCai+i1i)
前面的一项是停在 ( 0 , j ) (0,j) (0,j)的答案,后面那项是停在 ( j , 0 ) (j,0) (j,0)的答案.
注意这里计算的都只是 a i a_i ai的贡献.
a i a_i ai增加 1 1 1的时候,式子前后都只增加了 1 1 1项,所以可以 O ( 1 ) O(1) O(1)求出变化的贡献.
总的复杂度就是 O ( a + n ) O(a+n) O(a+n).

Code

#include 
#include 
#include 
#define ll long long

using namespace std;

const int N = 5e5 + 5,mo = 323232323;

int n;
int a[N];
ll ans;
ll frac[N << 1],inv[N << 1],g[N << 1],inv_2[N],c[N];

void prepare()
{
	frac[0] = frac[1] = inv[0] = inv[1] = 1;
	for (int i = 2 ; i < N << 1 ; i++) inv[i] = ((mo - mo / i) * inv[mo % i] % mo);
	for (int i = 2 ; i < N << 1 ; i++) frac[i] = frac[i - 1] * i % mo,inv[i] = inv[i - 1] * inv[i] % mo;
	inv_2[0] = 1;
	for (int i = 1 ; i < N ; i++) inv_2[i] = inv_2[i - 1] * inv[2] % mo;
}

ll C(int m,int n)
{
	return frac[n] * inv[m] % mo * inv[n - m] % mo;
}

void solve()
{
	for (int i = 0 ; i < N ; i++)
	{
		c[i] = C(i,i + a[1] - 1) * inv_2[a[1] + i] % mo;
		g[i] = c[i] * i % mo;
		if (i) c[i] = (c[i] + c[i - 1]) % mo,g[i] = (g[i] + g[i - 1]) % mo;
	}
}

int main()
{
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	scanf("%d",&n);
	for (int i = 1 ; i <= n ; i++) scanf("%d",&a[i]);
	prepare();
	solve();
	ans = a[1];
	for (int i = 2 ; i <= n ; i++)
		ans = (ans + g[a[i]] + 1LL * (mo + 1 - c[a[i]]) * a[i]) % mo;
	printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(DP)