原题的图。不知道干什么用。
题目大意
有 n n n 只蚂蚁等距站成一排,两边有挡板。每只蚂蚁等概率地选择一个运动方向(向左或向右),初始重量为 1 1 1. 每只蚂蚁速度相同,当两只蚂蚁相遇,重量大的吃掉重量小的,重量相等则向左的吃掉向右的,重量变为两者之和。若撞到挡板,则向相反方向运动。求每只蚂蚁活到最后的概率 m o d 1 0 9 + 7 \bmod 10^9+7 mod109+7.
题解
对于每只蚂蚁需求存活的方案数除以总方案数。
首先明确:第 1 1 1 只蚂蚁和第 n n n 只蚂蚁可以认为方向确定。第 1 1 1 只蚂蚁一定向右,第 n n n 只蚂蚁一定向左。第 1 1 1 只蚂蚁向左或向右的效果都是一样的,如果向左则会撞墙反弹。也即第 1 1 1 只蚂蚁有向左向右两种方案,而这两种方案得出的结果是完全一样的。当第 2 2 2 只蚂蚁到第 n n n 只蚂蚁的方向都确定,若第 1 1 1 只蚂蚁向右有 x x x 种方案,那么向左也是 x x x 种方案,总共 2 x 2x 2x 种方案,答案为 2 x 2 n = x 2 n − 1 \frac {2x}{2^n}=\frac x {2^{n-1}} 2n2x=2n−1x. 也就可以看做方向确定。第 n n n 只蚂蚁同理。
考虑对于蚂蚁 i i i,它必须吃掉除了它之外的所有蚂蚁。若 i i i 左边第一只向左走的蚂蚁为 j j j,那么无论怎样, 1 1 1 到 j j j 都会变成一只重量为 j j j 的蚂蚁, i i i 的重量为 i − j i-j i−j. 如果 i i i 要存活,那么满足 j ≤ i − j j\le i-j j≤i−j,也即 j ≤ ⌊ i 2 ⌋ j\le \lfloor \frac i 2\rfloor j≤⌊2i⌋. 那么第 2 2 2 只蚂蚁到第 ⌊ i 2 ⌋ \lfloor \frac i 2\rfloor ⌊2i⌋ 只蚂蚁方向任意,第 ⌊ i 2 ⌋ + 1 \lfloor \frac i 2\rfloor+1 ⌊2i⌋+1 只蚂蚁到第 i − 1 i-1 i−1 只蚂蚁一定向右,那么 i i i 左边的方案数为 2 ⌊ i 2 ⌋ − 1 2^{\lfloor \frac i 2\rfloor-1} 2⌊2i⌋−1.
接下来考虑右边的方案。
设 i i i 右边第一只向左走的蚂蚁为 j j j,同上推理得 i i i 存活的条件为 j < 2 i j<2i j<2i.
设 f i f_i fi 为蚂蚁 i i i 存活时右边的方案数。
有 f i = ∑ j = i + 1 2 i − 1 f j f_i=\sum\limits_{j=i+1}^{2i-1}f_j fi=j=i+1∑2i−1fj.
这东西显然可以前缀和 O ( n ) O(n) O(n) 搞。
a n s i = 2 ⌊ i 2 ⌋ − 1 × f i 2 i − 2 = 2 ⌊ i 2 ⌋ × f i 2 i − 1 ans_i=\dfrac {2^{\lfloor \frac i 2\rfloor-1}\times f_i}{2^{i-2}}=\dfrac {2^{\lfloor \frac i 2\rfloor}\times f_i}{2^{i-1}} ansi=2i−22⌊2i⌋−1×fi=2i−12⌊2i⌋×fi.
直接快速幂 O ( n log n ) O(n\log n) O(nlogn) 有概率寄掉。预处理后时间复杂度为 O ( n ) O(n) O(n). 总的时间复杂度为 O ( ∑ n ) O(\sum n) O(∑n).
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1000005;
const LL mod = 1e9 + 7;
int T, n;
LL f[N], sum[N], inv[N], mi[N];
LL qpow(LL x, LL y) {
LL sum = 1;
while (y) {
if (y & 1) sum = sum * x % mod;
x = x * x % mod, y >>= 1;
}
return sum;
}
void init() {
mi[0] = inv[0] = 1;
LL inv2 = qpow(2, mod - 2);
for (int i = 1; i < N; i++) mi[i] = mi[i - 1] * 2 % mod, inv[i] = inv[i - 1] * inv2 % mod;
}
int main() {
init();
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
memset(f, 0, sizeof(f)), memset(sum, 0, sizeof(sum)), f[n] = sum[n] = 1, sum[n + 1] = 0;
for (int i = n - 1; i >= 1; i--) f[i] = ((sum[i + 1] - sum[min(n + 1, i * 2)]) % mod + mod) % mod, sum[i] = (sum[i + 1] + f[i]) % mod;
for (int i = 1; i <= n; i++) printf("%lld\n", f[i] * mi[i / 2] % mod * inv[n - 1] % mod);
}
return 0;
}