口吃

口吃

题目描述

Zaoly 要讲一句话,这句话有 n 个字,他要一个字一个字讲出来。奈何 Zaoly 口吃:

  • 讲到第 1 个字时,下一个要讲的字有 a1a1+b1 的概率前进到第 2 个字,有 b1a1+b1 的概率仍是第 1 个字。
  • 讲到第 i (2in1) 个字时,下一个要讲的字有 ai2ai2+2aibi+bi2 的概率前进到第 i+1 个字,有 2aibiai2+2aibi+bi2 的概率仍是第 i 个字,有 bi2ai2+2aibi+bi2 的概率倒退到第 i1 个字。
  • 讲到第 n 个字时,讲话完毕,停止讲话。

直到讲话完毕,Zaoly 总共讲出的字数的期望是多少?

输入描述:

第一行输入一个整数 n (2n105),表示这句话有 n 个字。

第二行输入 n1 个数 a1,a2,,an1 (1ai109) 表示数组。

第三行输入 n1 个数 b1,b2,,bn1 (1bi109) 表示数组。

保证对于所有的 1in1,都满足 ai+bi109+7

输出描述:

输出总共讲出的字数的期望。

可以证明答案可以表示为一个不可约分数 pq,为了避免精度问题,请直接输出整数 (pq1modM) 作为答案,其中 M=109+7q1 是满足 q×q11(modM) 的整数。

示例1

输入

2
1
1

输出

3

说明

说完第一个字后,有 1/2 的概率直接前进到下一个字,有 1/4 的概率多讲一个字,有 1/8 的概率多讲两个字

说出总字数的期望为 1+12×1+14×2+18×3+116×4+=3

示例2

输入

3
1 1
1 1

输出

9

示例3

输入

6
1 2 3 4 5
5 4 3 2 1

输出

800000096

 

解题思路

  一开始往贡献法的思路想没做出来,dp 也不会。这 dp 的定义还挺难想到的,定义 f(i) 表示从第 i 个字开始读的期望值,转移方程就是f(i)=ai2(ai+bi)2f(i+1)+2aibi(ai+bi)2f(i)+bi2(ai+bi)2f(i1)+1

  容易看出这个转移是存在环的。注意到读第 1 个字是不会往前退的,此时有

f(1)=a1a1+b1f(2)+b1a1+b1f(1)+1f(1)=f(2)+a1+b1a1

  对于 f(2) 就有

f(2)=a22(a2+b2)2f(3)+2a2b2(a2+b2)2f(2)+b22(a2+b2)2f(1)+1=a22(a2+b2)2f(3)+2a2b2(a2+b2)2f(2)+b22(a2+b2)2(f(2)+a2+b2a2)+1f(2)=a22a22+b22b221f(3)+b22a1+b1a1+(a2+b2)2a22+b22b221

  这样就可以消去环,进一步推导可以发现转移方程都是 f(i)=xif(i+1)+yi 的形式,其中 xi=ai2ai2+bi2bi2xi1yi=bi2yi1+(ai+bi)2ai2+bi2bi2xi1。初始时 x1=1y1=a1+b1a1

  由于 f(n)=1,因此我们可以先计算出所有的 xiyi,然后倒着 dp 依次求出 f(i)

  AC 代码如下,时间复杂度为 O(nlogM)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 1e5 + 5, mod = 1e9 + 7;

int a[N], b[N];
int x[N], y[N];
int f[N];

int qmi(int a, int k) {
    int ret = 1;
    while (k) {
        if (k & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod;
        k >>= 1;
    }
    return ret;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    for (int i = 1; i < n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i < n; i++) {
        cin >> b[i];
    }
    x[1] = 1, y[1] = LL(a[1] + b[1]) * qmi(a[1], mod - 2) % mod;
    for (int i = 2; i < n; i++) {
        int t = qmi((1ll * a[i] * a[i] + 1ll * b[i] * b[i] - 1ll * b[i] * b[i] % mod * x[i - 1]) % mod, mod - 2);
        x[i] = 1ll * a[i] * a[i] % mod * t % mod;
        y[i] = (1ll * b[i] * b[i] % mod * y[i - 1] + LL(a[i] + b[i]) * (a[i] + b[i])) % mod * t % mod;
    }
    f[n] = 1;
    for (int i = n - 1; i; i--) {
        f[i] = (1ll * x[i] * f[i + 1] + y[i]) % mod;
    }
    cout << f[1];
    
    return 0;
}

 

参考资料

  牛客周赛60题目讲解:https://www.bilibili.com/video/BV1BR4SeYEdi?p=5

posted @   onlyblues  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-09-18 Smallest Subarrays With Maximum Bitwise OR
2022-09-18 解方程
2022-09-18 击中战舰
Web Analytics
点击右上角即可分享
微信分享提示