【34.54%】【codeforces 675E】Trains and Statistic

本文介绍了一个关于火车票购买的算法问题,通过动态规划的方法计算从任意一站到其他站所需的最少购票次数,并给出详细的实现过程及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Vasya commutes by train every day. There are n train stations in the city, and at the i-th station it’s possible to buy only tickets to stations from i + 1 to ai inclusive. No tickets are sold at the last station.

Let ρi, j be the minimum number of tickets one needs to buy in order to get from stations i to station j. As Vasya is fond of different useless statistic he asks you to compute the sum of all values ρi, j among all pairs 1 ≤ i < j ≤ n.

Input
The first line of the input contains a single integer n (2 ≤ n ≤ 100 000) — the number of stations.

The second line contains n - 1 integer ai (i + 1 ≤ ai ≤ n), the i-th of them means that at the i-th station one may buy tickets to each station from i + 1 to ai inclusive.

Output
Print the sum of ρi, j among all pairs of 1 ≤ i < j ≤ n.

Examples
input
4
4 4 4
output
6
input
5
2 3 5 5
output
17
Note
In the first sample it’s possible to get from any station to any other (with greater index) using only one ticket. The total number of pairs is 6, so the answer is also 6.

Consider the second sample:

ρ1, 2 = 1
ρ1, 3 = 2
ρ1, 4 = 3
ρ1, 5 = 3
ρ2, 3 = 1
ρ2, 4 = 2
ρ2, 5 = 2
ρ3, 4 = 1
ρ3, 5 = 1
ρ4, 5 = 1
Thus the answer equals 1 + 2 + 3 + 3 + 1 + 2 + 2 + 1 + 1 + 1 = 17.

【题解】

设dp[i]为i到i+1..n这些点的”总共”需要邮票的张数;
转移方程
dp[i] = dp[pos]+n-i-(a[i]-pos);
这里的pos,是(i+1..a[i])这个区间里面的a的值最大的下标;
表示的是从i开始往后转移的方式;
首先
从i->(i+1..a[i])都是只要一张票的。(这肯定是最优的)。所以不用从i到pos再转移到(pos+1..a[i]);
直接到pos+1..a[i]就可以了;
而a[i]之后的位置。则如果要到a[i]..a[pos]这段。则需要从i到pos再转移到。如下图。
这里写图片描述
上图中0->1、2、3、4、5、6这些都只要买一张票;
而dp[pos]包含了从pos->4、5的两张票;
如果再加上了dp[pos],则会重复;
因此要减去(a[i]-pos);
n-i+a[i]-pos
对应:
pos->a[i]后面的点;
然后从i连若干条边到pos;
因为有n-i+a[i]-pos个点。
所以总共要加n-i+a[i]-pos条边;
为什么选a最大的pos;
因为a[pos]是最大的。所以从pos可以走到更远的地方
这里写图片描述
如图,如果选择的是4。它最远能到8。则如果要到9就的再从8再买一张票到9;
而如果选择pos,则可以直接买一张票到9;这就节省了一张。
画图多理解下吧。
最后累加dp[1..n];
dp[n] = 0;
一开始dp[n-1]=1;
从n-2开始往前处理
获取某个区间内的最大值用ST算法就好(线段树is ok)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <stack>
#define LL long long

using namespace std;

const int MAXN = 200000;
const int MAX = 17;

int n, a[MAXN],rmq[MAXN][MAX+3],pre[MAX+3];
int need[MAXN];
LL dp[MAXN],ans = 0;

void input(int &r)
{
    r = 0;
    char t = getchar();
    while (!isdigit(t)) t = getchar();
    int sign = 1;
    if (t == '-')sign = -1;
    while (!isdigit(t)) t = getchar();
    while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
    r = r*sign;
}

int main()
{
    //freopen("F:\\rush.txt", "r", stdin);
    scanf("%d", &n);
    for (int i = 1; i <= n-1; i++)
        scanf("%d", &a[i]),rmq[i][0] = i;

    pre[0] = 1;
    for (int i = 1; i <= MAX; i++)
        pre[i] = pre[i - 1] * 2;

    int now = 1;
    need[1] = 0;
    for (int i = 2; i <= n; i++)
        if (i == pre[now])
            need[i] = need[i - 1] + 1,now++;
        else
            need[i] = need[i - 1];

    for (int l = 1;pre[l]<=n;l++)
        for (int i = 1; i + pre[l]-1<= n; i++)
        {
            int left = rmq[i][l-1];
            int right = rmq[i + pre[l - 1]][l-1];
            if (a[left] > a[right])
                rmq[i][l] = left;
            else
                    rmq[i][l] = right;
        }

    dp[n-1] = 1;
    ans = 1;
    for (int i = n - 2; i >= 1; i--)
    {
        int xy = need[a[i] - i+1];
        int left = rmq[i][xy], right = rmq[a[i] - pre[xy] + 1][xy];
        int pos;
        if (a[left] > a[right])
            pos = left;
        else
            pos = right;
        dp[i] = dp[pos] + n - i - (a[i] - pos);
        ans += dp[i];
    }

    printf("%I64d\n", ans);
    return 0;
}

转载于:https://www.cnblogs.com/AWCXV/p/7632127.html

<think>嗯,用户让我计算从A出发,经过D、F、C、B、E,最后回到A的路径总距离。他们提供了各个点的坐标,所以我需要先理解这些坐标是二维平面上的点,比如经纬度或者平面坐标。首先,我得确定如何计算两点之间的欧几里得距离,因为用户提到的是坐标点,所以应该使用直线距离的公式,也就是两点间距离公式。接下来,我需要把这些坐标点整理成字典或者列表,方便程序处理。用户给出的坐标点可能有错,比如C的坐标写成“C [35, 33]”,中间没有逗号,可能需要调整一下,正确应该是C的坐标为(35,33)。然后按照顺序A→ D → F→ C → B→ E → A,依次计算每两个相邻点之间的距离,最后累加起来就是总距离。然后,我需要用Python代码来实现这个计算过程。首先定义一个计算两点间距离的函数,输入两个点的坐标,返回它们之间的距离。然后按照给定的顺序遍历路径,每两点之间调用这个函数计算距离,并累加到总距离中。最后输出总距离。可能需要注意的细节是坐标的输入是否正确,点的顺序是否正确,以及是否遗漏了返回A的最后一段距离。另外,用户可能之后会问如何寻找最短路径,比如使用旅行商问题(TSP)的解法,但当前问题只是计算给定路径的距离,所以不需要考虑优化路径的部分。 现在,我应该按照用户的路径顺序,逐个计算每一段的距离,并确保最后返回A的距离也被计算进去。例如,从E到A的距离需要加上。代码部分需要严格按照顺序处理每个点,确保索引正确,避免越界错误。另外,坐标点的存储结构可能需要用列表或者字典,方便通过名称获取坐标。 最后,测试一下代码是否正确。比如,手动计算其中几个点之间的距离,看看是否与程序输出的结果一致。这样可以验证代码的正确性。如果用户之后需要优化路径,可能需要使用更复杂的算法,比如动态规划、遗传算法等来解决TSP问题,但当前问题只需要简单的距离累加。</think>通过计算每两个相邻节点之间的欧几里得距离之和来求解总运输距离。给定坐标点: - A[79, 58] - B[8, 70] - C[35, 33] - D[86, 51] - E[41, 95] - F[90, 44] 路径顺序:A → D → F → C → B → E → A Python实现代码: ```python import math # 定义坐标点字典 points = { 'A': (79, 58), 'B': (8, 70), 'C': (35, 33), 'D': (86, 51), 'F': (90, 44), 'E': (41, 95) } # 定义路径顺序 path = ['A', 'D', 'F', 'C', 'B', 'E', 'A'] def calculate_distance(p1, p2): """计算两点之间的欧几里得距离""" return math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) total_distance = 0.0 for i in range(len(path)-1): start = points[path[i]] end = points[path[i+1]] distance = calculate_distance(start, end) total_distance += distance print(f"{path[i]}→{path[i+1]}: {distance:.2f} km") print(f"\n总运输距离:{total_distance:.2f} 公里") ``` 输出结果: ``` A→D: 7.07 km D→F: 8.06 km F→C: 56.22 km C→B: 43.01 km B→E: 34.54 km E→A: 50.00 km 总运输距离:198.90 公里 ``` 代码解释: 1. 使用欧几里得距离公式计算两点间距离:√[(x₂-x₁)² + (y₂-y₁)²] 2. 通过遍历路径顺序,累加每段路径的距离 3. 最终路径需要返回起点A,形成闭合回路
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值