【题解】CF1737E Ela Goes Hiking

题目(cf)

题目(洛谷)

原题的图。不知道干什么用。

题目大意

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=2n1x. 也就可以看做方向确定。第 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 ij. 如果 i i i 要存活,那么满足 j ≤ i − j j\le i-j jij,也即 j ≤ ⌊ i 2 ⌋ j\le \lfloor \frac i 2\rfloor j2i. 那么第 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 i1 只蚂蚁一定向右,那么 i i i 左边的方案数为 2 ⌊ i 2 ⌋ − 1 2^{\lfloor \frac i 2\rfloor-1} 22i1.

接下来考虑右边的方案。

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+12i1fj.

这东西显然可以前缀和 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=2i222i1×fi=2i122i×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;
}

END

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值