CF1523E Crypto Lights

Deltix Round, Spring 2021 E. Crypto Lights

题目大意

\(n\) 个台灯初始时都是暗的,每次等概率随机一个暗台灯将其点亮,若点亮后存在一个长度为 \(k\) 的连续段有大于一个台灯被点亮则立刻停止,求期望点亮多少台灯。答案对 \(10^9+7\) 取模。

  • \(2\le k\le n\le 10^5\)

分析

对式子的转化太妙啦,我们来欣赏一下。

首先构造一个概率\(p_i\):最终点亮了i盏灯后流程结束的概率。这样便于我们去计算最后的期望:

\[E = \sum_{i=1}^{n}ip_i \]

但是这个概率,非常的不好计算,因为,我们难以处理\(p_i\),从其定义出发,我们需要考虑的选择太多了,极多的摆放方式使得我们无法下手计算。

因此,这就提示我们能否,从反向思考。那反向是什么?比较容易想到的反向是,我们考虑,放置第i个灯无法结束流程,但我们发现其依旧很难下手,因为其中情况等于说是\(\sum_{j!=i}p_j\),这依旧没办法算。

这里就非常妙的对式子进行了等价变化。

我们观察一下式子

\[E\ =\ p_1\ +\ 2P_2\ +\ 3P3 +\ ...\ +\ np_n \]

我们发现可以将式子转化为后缀和的和,后缀和为\(s_i=\sum_{j=i}^{n}p_j\)。则式子变为。

\[E\ = \ \sum_{i=1}^{n}s_i \]

我们考虑\(s_i\)的组合意义。其即为点亮i-1个灯无法结束

而这个的组合含义较为好算,我们考虑先放置i-1个灯,并且其无法结束流程。

我们将在i-2个间隔中,放置(k-1)*(i-2)个灯,接着我们从的n-(k-1)*(i-2)个位置挑选,放置亮灯的i-1的位置。我们可以这样理解,从n-(k-1)*(i-2)个位置选完后,直接向i-2个空隙插入k-1个小球。

如果这样不好理解,我们考虑从隔板法的角度思考,总共有i个盒子,其中除左右两个两个盒子外,每个盒子至少需要k-1个小球,总共有n个小球,则我们得到一个不定方程。

\[x_1\ + x_2\ +\ ...\ +\ x_{i}\ =\ n (x_1\geq0,x_2\geq0,x_3,x_4,x_i\geq k-1) \]

然后就结束啦。

这两种理解都是的答案都是。

\[E\ =\ \sum_{i=1}^{n}\frac{C(n-(k-1)*(i-2),i-1)}{C(n,i-1)} \]

其中特别需要说一下,其中i=1时,需要注意一下,分母上的n-(k-1)(i-2)会大于等于n,因此我们直接特判一下就好,\(s_i=1\)

来看看代码。

Ac_code

#include<bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 1e5 + 10,mod = 1e9 + 7;

template<int T>
struct ModInt {
    const static int mod = T;
    int x;
    ModInt(int x = 0) : x(x % mod) {}
    int val() { return x; }
    ModInt operator + (const ModInt &a) const { int x0 = x + a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); }
    ModInt operator - (const ModInt &a) const { int x0 = x - a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); }
    ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
    ModInt operator / (const ModInt &a) const { return *this * a.inv(); }
    void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; if (x < 0) x += mod;}
    void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; if (x >= mod) x -= mod;}
    void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; }
    void operator /= (const ModInt &a) { *this = *this / a; }
    friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;}
    
    ModInt pow(int n) const {
        ModInt res(1), mul(x);
        while(n){
            if (n & 1) res *= mul;
            mul *= mul;
            n >>= 1;
        }
        return res;
    }
    
    ModInt inv() const {
        int a = x, b = mod, u = 1, v = 0;
        while (b) {
            int t = a / b;
            a -= t * b; swap(a, b);
            u -= t * v; swap(u, v);
        }
        if (u < 0) u += mod;
        return u;
    }
    
};
typedef ModInt<mod> mint;

mint fact[N],infact[N];
int ans = 0;

void init()
{
    fact[0] = infact[0] = 1;
    for(int i=1;i<N;i++) fact[i] = fact[i-1]*mint(i);
    infact[N-1] = fact[N-1].inv();
    for(int i=N-2;i;i--) infact[i] = infact[i+1]*mint(i+1);
}

mint C(LL a,LL b)
{
    if(a<b) return mint(0);
    return fact[a]*infact[a-b]*infact[b];
}

int main()
{
    init();
    int T;cin>>T;
    while(T--)
    {
        LL n,k;cin>>n>>k;
        mint ans = 1;
        for(int i=2;i<=n;i++)
            ans += C(n-(k-1)*(i-2),i-1)*(C(n,i-1).inv());
        cout<<ans<<'\n';
    }
    return 0;
}
posted @ 2022-09-13 23:33  艾特玖  阅读(32)  评论(0)    收藏  举报