ICPC网络预选赛第二场个人题解


目前只更新了I题,别的有空补上。

I Impatient Patient

You accidentally sprained your ankle, and now you are facing a long recovery phase. Initially, you are at stage 0, and your recovery progresses until you reach stage n.

Each day, if you rest properly, you advance by exactly one stage. So it takes n days for you to recover, that is, if you do not do anything improper.

However, instead of resting, you have the option to challenge yourself, which also takes one day. If you are at stage i and succeed, you will instantly recover. However, if you fail, you will regress to stage a i a_i ai (0≤ a i a_i aii). The probability of success is p i p_i pi.

Now, you are wondering what the expected time required for your recovery would be, assuming you adopt the best strategy.

Input

The first line contains a positive integer T (1≤T 1 0 4 10^4 104), denoting the number of test cases.

For each test case:

  • The first line contains one integer n (1≤n≤5× 1 0 5 10^5 105), denoting the number of stages of recovery.
  • The next line contains n integers a 0 a_0 a0, a 1 a_1 a1,⋯, a n − 1 a_{n-1} an1 (0≤ a i a_i aii), denoting the stage you will go back to if you fail at stage i.
  • The next line contains n integers q 0 q_0 q0, q 1 q_1 q1,⋯, q n − 1 q_{n-1} qn1 (0≤ q i q_i qi≤105), where p i p_i pi= q i q_i qi/ 1 0 5 10^5 105 denotes the probability of success at stage i.

It is guaranteed that ∑n≤5× 1 0 5 10^5 105.

Output

For each test case, print one decimal number, denoting the expected time needed for you to recover if you take the best strategy. The answer will be considered correct if its absolute or relative error is less than 10^-9.

Sample Input

inCopy code5
1
0
0
3
0 1 2
99999 0 0
3
0 1 2
0 50001 100000
5
0 1 2 0 1
21735 25194 37976 89936 99999
8
0 0 2 2 4 3 4 1
12839 27084 17777 35472 31951 64686 96898 0

Sample Output

outCopy code1.000000000000
1.000010000100
2.999960000800
4.447607187333
7.096039133935

Code Size Limit

64 KB

Time Limit

1000 ms

Memory Limit

512 MB

翻译:

你不小心扭伤了脚踝,现在面临着漫长的康复期。最初,你处于第 0 0 0阶段,康复会一直进行,直到达到第 n n n阶段。

每天,如果你适当休息,你会前进一阶段。所以如果你不采取任何不当行动,你需要n天来康复。

然而,除了休息,你还有挑战自己的选项,这也需要一天的时间。如果你处于第 i i i阶段并且成功了,你将立刻康复。但是,如果失败了,你将回到第 a i a_i ai 阶段 ( 0 ≤ a i ≤ i ) (0 \leq a_i \leq i) 0aii)。成功的概率是 p i p_i pi

现在,你想知道,假设你采取最佳策略,需要多长时间才能康复。

输入

第一行包含一个正整数 T ( 1 ≤ T ≤ 1 0 4 ) T(1≤T≤10^4) T(1T104),表示测试用例的数量。

对于每个测试用例:

第一行包含一个整数 n ( 1 ≤ n ≤ 5 ⋅ 1 0 5 ) n(1 \leq n \leq 5 \cdot 10^5) n1n5105,表示康复阶段的数量。 接下来一行包含 n n n个整数 a 0 a_0 a0, a 1 a_1 a1,⋯, a n − 1 ( 0 ≤ a i ≤ i ) a_{n-1}(0 \leq a_i \leq i) an1(0aii) ,表示如果在第 i i i阶段失败,你将回到的阶段 i i i。 接下来一行包含 n n n个整数 q 0 q_0 q0, q 1 q_1 q1,⋯, q n − 1 q_{n−1} qn1 ( 0 ≤ q i ≤ 1 0 5 ) (0 \leq q_i \leq 10^5) (0qi105),其中 p i = q i 1 0 5 p_i = \frac{q_i}{10^5} pi=105qi表示第 i i i阶段成功的概率。 保证 ∑ n ≤ 5 ⋅ 1 0 5 \sum n\leq5\cdot10^5 n5105

输出

对于每个测试用例,输出一个十进制数,表示如果采取最佳策略,你需要康复的预期时间。如果答案的绝对或相对误差小于 1 0 − 9 10^{-9} 109,将被视为正确。

示例输入

1
0
0
3
0 1 2
99999 0 0
3
0 1 2
0 50001 100000
5
0 1 2 0 1
21735 25194 37976 89936 99999
8
0 0 2 2 4 3 4 1
12839 27084 17777 35472 31951 64686 96898 0

示例输出

1.000000000000
1.000010000100
2.999960000800
4.447607187333
7.096039133935

代码极限长度

64 KB

时间极限长度

1000 ms

内存极限大小

512 MB

个人题解:

思路:

刚看到这题的时候就跑偏了,想到概率 d p dp dp去了,纠结了很久对于当前阶段如果挑战成功了该怎么算期望。因为直接结束,状态不会移了,状态转移方程到这里直接断了。后来仔细思考了一下,发现不用那么复杂。

因为对于每个阶段来说,我们只有两种选择,要么不挑战,进入下一阶段,要么一直挑战,直到痊愈。除此以外的任何选择都是更差的。如果在阶段 x x x挑战失败了,我们经过一定的天数回到当前阶段,并且选择不挑战然后进入下一阶段 x + 1 x+1 x+1,我们对于 x + 1 x+1 x+1阶段的任何决策而言,期望都会增加 ( 1 − p x ) ∗ ( x − a x ) (1-p_x)*(x-a_x) (1px)(xax)。( p x p_x px x x x阶段挑战成功的概率, a x a_x ax x x x阶段挑战失败退回的阶段)由此可见,一定存在一个最优阶段,到达这个阶段一直进行挑战就是最优策略。

我们假设阶段 i i i最优阶段,对于阶段 i i i来说,如果挑战失败了,直接退回 a i a_i ai阶段,如果我们要回到阶段 i i i需要花费 i − a i i-a_i iai天。那么阶段 i i i的期望就 = = =到达当前阶段所需要的天数 + + +挑战失败回到当前阶段的期望 + + +挑战成功花费的时间。转化成公式就是 e x p e c t i = i + p i 1 − p i ⋅ ( i − a i + 1 ) + 1 expect_i=i+\frac{p_i}{1-p_i}\cdot(i-a_i+1)+1 expecti=i+1pipi(iai+1)+1

其中 1 − p i p i ⋅ ( i − a i + 1 ) \frac{1-p_i}{p_i}\cdot(i-a_i+1) pi1pi(iai+1)是回到 i i i阶段的期望。计算方式如下:

设成功的概率为 p i p_i pi,那么失败的概率为 1 − p i 1-p_i 1pi。那么连续失败 n n n次的概率为 ( 1 − p i ) n (1-p_i)^n (1pi)n。由此易得失败的期望为 ∑ j = = 1 n ( 1 − p i ) j , ( lim ⁡ n → + ∞ ) \sum_{j==1}^{n}{(1-p_i)^j},(\lim_{n \to +\infty}) j==1n(1pi)j,(limn+),解得原式 = 1 − p i p i =\frac{1-p_i}{p_i} =pi1pi。(没学过极限的可以把它当做一个等比数列,等比数列的求和公式是 S n = a 1 ( 1 − q n ) 1 − q S_n=\frac{a_1(1-q^n)}{1-q} Sn=1qa1(1qn) q q q就等于 1 − p i 1-p_i 1pi,由于 q q q小于 1 1 1时( q q q一定小于 1 1 1,一定失败的情况不需要考虑),它的 + ∞ +\infty +次方无限接近于 0 0 0就等于 0 0 0,它的 a 1 a_1 a1就是 q q q。这样我们就可以化简得到公式 q 1 − 1 \frac{q}{1-1} 11q,把 q = p i − 1 q=p_i-1 q=pi1代入就得到了公式 1 − p i p i \frac{1-p_i}{p_i} pi1pi i − a i i-a_i iai是回到当前阶段所需要花费的时间,里面的 + 1 +1 +1是进行挑战需要花费一天时间。失败期望乘以回到当前阶段花费时间就是挑战失败后回到当前阶段的期望。

得出公式以后,我们就只需要把所有阶段的期望枚举出来然后找到它们之间的最小值并输出就行。

代码

C++
#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

void solve() {
    int n, temp;
    cin >> n;
    vector<int> a(n);
    vector<double> q(n);
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    for (int i = 0; i < n; i++)
    {
        cin >> temp;
        p[i] = temp / 1e5;//计算成功的概率
    }
    double ans = n;
    double now;
    for (int i = 0; i < n; i++)
    {
        if (!p[i])//如果不能成功,直接跳过这个阶段
            continue;
        now = i + ((1 - p[i]) / p[i]) * (i - a[i] + 1) + 1;//计算阶段i的期望
        ans = min(ans, now);
    }
    printf("%.12f\n", ans);
}

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		solve();
	}
	return 0;
}
C
#include <stdio.h>

void solve() 
{
    int n, temp;
    scanf("%d", &n);
    int a[n];
    double p[n];
    
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &a[i]);
    }
    
    for (int i = 0; i < n; i++) 
    {
        scanf("%d", &temp);
        p[i] = (double)temp / 1e5;  // 计算成功的概率
    }
    
    double ans = n;
    double now;
    
    for (int i = 0; i < n; i++) {
        if (p[i] == 0) // 如果不能成功,直接跳过这个阶段
            continue;
        now = i + ((1 - p[i]) / p[i]) * (i - a[i] + 1) + 1;  // 计算阶段i的期望
        ans = ans < now ? ans : now;
    }
    
    printf("%.12f\n", ans);
}

int main() 
{
    int t;
    scanf("%d", &t);
    while (t--) 
    {
        solve();
    }
    return 0;
}
Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) 
    {
        Scanner scanner = new Scanner(System.in);
        int t = scanner.nextInt();
        scanner.nextLine();  // 消耗换行符

        while (t-- > 0) 
        {
            solve(scanner);
        }
    }
    
    public static void solve(Scanner scanner) 
    {
        int n = scanner.nextInt();
        scanner.nextLine();  // 消耗换行符
        int[] a = new int[n];
        double[] p = new double[n];
    
        for (int i = 0; i < n; i++) 
        {
            a[i] = scanner.nextInt();
        }
    
        scanner.nextLine();  // 消耗换行符
    
        for (int i = 0; i < n; i++)
        {
            int temp = scanner.nextInt();
            p[i] = (double) temp / 1e5;  // 计算成功的概率
        }
    
        double ans = n;
        double now = 0;
        for (int i = 0; i < n; i++)
        {
            if (p[i] == 0) // 如果不能成功,直接跳过这个阶段
                continue;
            now = i + ((1 - p[i]) / p[i]) * (i - a[i] + 1) + 1;  // 计算阶段i的期望
            ans = Math.min(ans, now);
        }
    
        System.out.printf("%.12f\n", ans);
    }

}
Python
def solve():
    n = int(input())
    a = list(map(int, input().split()))
    p = list(map(int, input().split()))
    p = [temp / 1e5 for temp in p]  # 计算成功的概率

    ans = n
    now = 0
    for i in range(n):
        if p[i] == 0:  # 如果不能成功,直接跳过这个阶段
            continue
        now = i + ((1 - p[i]) / p[i]) * (i - a[i] + 1) + 1  # 计算阶段i的期望
        ans = min(ans, now)
    
    print(f"{ans:.12f}")

t = int(input())
for _ in range(t):
    solve()
  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值