REAL689 小红的点赞
题目链接
题目描述
小红有 篇笔记,初始点赞数分别为
。在每个时间单位,都会有一篇随机的笔记(
篇中的任意一篇,概率均等)点赞数加 1。
问:当第一次出现所有笔记的点赞数均为偶数时,所有笔记的总点赞数之和的期望是多少?
答案需要对 取模。
解题思路
这是一个求解期望值的过程,通常可以通过动态规划(特别是期望 DP)来解决。
-
状态定义
我们关心的是所有笔记点赞数的奇偶性。因此,系统的关键状态可以被简化为当前点赞数为奇数的笔记数量。设这个数量为
。我们的目标是从初始状态(有
个奇数赞的笔记)到达目标状态(
)。
-
期望的递推关系
设
为从当前有
个奇数赞笔记的状态,到第一次变为有 0 个奇数赞笔记的状态,所需要增加的点赞数的期望值。
当我们处于状态
时(
),下一步会发生什么:
-
我们随机选择一篇笔记点赞。
-
有
的概率,我们选中了一篇奇数赞的笔记。点赞后,它的赞数变为偶数。系统中奇数赞笔记的数量变为
。
-
有
的概率,我们选中了一篇偶数赞的笔记。点赞后,它的赞数变为奇数。系统中奇数赞笔记的数量变为
。
根据期望的线性性质,我们可以建立
的递推关系:
这里的
1
代表当前这一步增加的 1 个点赞。我们的目标是求解
,其中
是初始时点赞数为奇数的笔记数量。
-
-
解递推方程
这是一个包含
的三项递推式。直接求解比较困难。我们可以通过引入差分来简化它。
定义
。
对原递推式进行变形,可以得到
之间的关系:
这个新的递推式允许我们从
计算出
。
我们需要一个边界条件。考虑
的情况,此时所有笔记都是奇数赞。无论点赞哪一篇,都会使其变为偶数,状态必然转移到
。所以:
[
]
这意味着
。
有了这个边界条件,我们就可以从
开始,反向递推出
。
-
计算最终答案
-
首先,计算初始的总赞数
和奇数赞笔记的数量
。
-
如果
,说明初始状态就满足条件,期望增加的点赞数为 0,答案就是
。
-
否则,使用
和递推式计算出
。
-
我们要求的
可以通过差分求和得到:
(因为我们的目标状态是
,所以
)
-
最终的期望总赞数就是
。
注意:在计算
的过程中,涉及到除以
的操作。在模运算下,除法需要用乘以其模逆元来代替。由于模数
是一个质数,我们可以用费马小定理来计算模逆元。
-
代码
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
long long power(long long base, long long exp) {
long long res = 1;
base %= 1000000007;
while (exp > 0) {
if (exp % 2 == 1) res = (res * base) % 1000000007;
base = (base * base) % 1000000007;
exp /= 2;
}
return res;
}
long long modInverse(long long n) {
return power(n, 1000000007 - 2);
}
void solve() {
int n;
cin >> n;
vector<long long> a(n);
long long initial_sum = 0;
int odd_count = 0;
for (int i = 0; i < n; ++i) {
cin >> a[i];
initial_sum = (initial_sum + a[i]) % 1000000007;
if (a[i] % 2 != 0) {
odd_count++;
}
}
if (odd_count == 0) {
cout << initial_sum << endl;
return;
}
vector<long long> d(n + 1);
d[n] = 1;
for (int i = n - 1; i >= 1; --i) {
long long term1 = ((long long)(n - i) * d[i + 1]) % 1000000007;
long long numerator = (term1 + n) % 1000000007;
d[i] = (numerator * modInverse(i)) % 1000000007;
}
long long expected_steps = 0;
for (int i = 1; i <= odd_count; ++i) {
expected_steps = (expected_steps + d[i]) % 1000000007;
}
long long final_expectation = (initial_sum + expected_steps) % 1000000007;
cout << final_expectation << endl;
}
int main() {
solve();
return 0;
}
import java.util.Scanner;
public class Main {
static final int MOD = 1000000007;
public static long power(long base, long exp) {
long res = 1;
base %= MOD;
while (exp > 0) {
if (exp % 2 == 1) res = (res * base) % MOD;
base = (base * base) % MOD;
exp /= 2;
}
return res;
}
public static long modInverse(long n) {
return power(n, MOD - 2);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long initialSum = 0;
int oddCount = 0;
for (int i = 0; i < n; i++) {
long val = sc.nextLong();
initialSum = (initialSum + val) % MOD;
if (val % 2 != 0) {
oddCount++;
}
}
if (oddCount == 0) {
System.out.println(initialSum);
return;
}
long[] d = new long[n + 1];
d[n] = 1;
for (int i = n - 1; i >= 1; i--) {
long term1 = ((long) (n - i) * d[i + 1]) % MOD;
long numerator = (term1 + n) % MOD;
d[i] = (numerator * modInverse(i)) % MOD;
}
long expectedSteps = 0;
for (int i = 1; i <= oddCount; i++) {
expectedSteps = (expectedSteps + d[i]) % MOD;
}
long finalExpectation = (initialSum + expectedSteps) % MOD;
System.out.println(finalExpectation);
}
}
MOD = 1000000007
def power(base, exp):
res = 1
base %= MOD
while exp > 0:
if exp % 2 == 1:
res = (res * base) % MOD
base = (base * base) % MOD
exp //= 2
return res
def mod_inverse(n):
return power(n, MOD - 2)
def solve():
n = int(input())
a = list(map(int, input().split()))
initial_sum = sum(a) % MOD
odd_count = sum(1 for x in a if x % 2 != 0)
if odd_count == 0:
print(initial_sum)
return
d = [0] * (n + 1)
d[n] = 1
for i in range(n - 1, 0, -1):
term1 = ((n - i) * d[i + 1]) % MOD
numerator = (term1 + n) % MOD
d[i] = (numerator * mod_inverse(i)) % MOD
expected_steps = sum(d[i] for i in range(1, odd_count + 1)) % MOD
final_expectation = (initial_sum + expected_steps) % MOD
print(final_expectation)
if __name__ == "__main__":
solve()
算法及复杂度
-
算法:期望动态规划
-
时间复杂度:
,其中
是笔记的数量,
是模数。
- 计算初始状态耗时
。
- 计算
数组需要一个大小为
的循环,循环内部的主要开销是计算模逆元,使用快速幂需要
的时间。
- 最后求和需要
。
- 因此总时间复杂度为
。
- 计算初始状态耗时
-
空间复杂度:
。
- 需要一个大小为
的数组来存储差分值
。
- 需要一个大小为