Codeforces Round #353 (Div. 2)E. Trains and Statistic

本文解析了CodeForces上的一道题目E,通过动态规划的方法解决了关于火车票购买次数的最小化问题,并给出了详细的算法解释及代码实现。

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

链接:http://codeforces.com/contest/675/problem/E

题意:给定n个火车站的信息,a[i]表示能在第i站买到去i+1~a[i]之间任意一站的票(a[n]因为是终点站所以只给前n-1个站的信息),设f[i][j]表示从第i个站到第j个站至少要买多少次票。求sum=f[i][j](i<j)。

分析:CF上是有题解的,不过这个题解挺简练的,需要立即其中的含义。我们设dp[i]表示从第i站到i+1~n所有站所需要的最少次购票,那么有dp[i]=dp[m]-(a[i]-m)+n-i且m是i+1~a[i]之间的数且a[m]最大,如果有多个m取是dp[i]最小的那个。这是CF官方题解给出的dp方程式。首先我们理解一下为什么是取这个m,显然出题人的意思是dp[i]是要从这个a[m]最大上面去继承答案。我们简单画一下图就能发现这样一个大小关系:i<m<a[i]<a[m]<n。这个n-i显然是表示i+1~n每个站至少买一次票,那么哪些站多买了呢?m+1~a[i]这些站,因为之前m到这些站就买了一次票,这一次再买就重复了,于是就有-(a[i]-m)。那么怎么能保证a[i]+1~n的每一站是买的最少的票呢?那么就直接继承dp[m]中最小的买票次数就行啦。

代码:

[cpp]  view plain  copy
  1. #include<map>  
  2. #include<set>  
  3. #include<cmath>  
  4. #include<queue>  
  5. #include<bitset>  
  6. #include<math.h>  
  7. #include<cstdio>  
  8. #include<vector>  
  9. #include<string>  
  10. #include<cstring>  
  11. #include<iostream>  
  12. #include<algorithm>  
  13. #pragma comment(linker, "/STACK:102400000,102400000")  
  14. using namespace std;  
  15. const int N=100010;  
  16. const int MAX=1000000100;  
  17. const int mod=100000000;  
  18. const int MOD1=1000000007;  
  19. const int MOD2=1000000009;  
  20. const double EPS=0.00000001;  
  21. typedef long long ll;  
  22. const ll MOD=998244353;  
  23. const int INF=1000000010;  
  24. typedef double db;  
  25. typedef unsigned long long ull;  
  26. int a[N],f[N][20];  
  27. ll p[N],dp[N];  
  28. void deal(int n) {  
  29.     int i,j;  
  30.     for (i=1;i<=n;i++) f[i][0]=a[i];  
  31.     for (i=1;i<20;i++)  
  32.         for (j=1;j<=n;j++)  
  33.         if (j+(1<<i)-1<=n) f[j][i]=max(f[j][i-1],f[j+(1<<(i-1))][i-1]);  
  34.         else break ;  
  35. }  
  36. int find_mx(int l,int r) {  
  37.     int m=(int)log2(r-l+1);  
  38.     return max(f[l][m],f[r-(1<<m)+1][m]);  
  39. }  
  40. int main()  
  41. {  
  42.     int i,n,mx;  
  43.     ll ans=0;  
  44.     scanf("%d", &n);  
  45.     for (i=1;i<n;i++) scanf("%d", &a[i]);  
  46.     a[n]=n;deal(n);  
  47.     memset(p,-1,sizeof(p));  
  48.     dp[n]=0;p[n]=n;  
  49.     for (i=n-1;i;i--) {  
  50.         mx=find_mx(i+1,a[i]);  
  51.         dp[i]=p[mx]-a[i]+n-i;ans+=dp[i];  
  52.         if (p[a[i]]==-1) p[a[i]]=dp[i]+i;  
  53.         else p[a[i]]=min(p[a[i]],dp[i]+i);  
  54.     }  
  55.     printf("%I64d\n", ans);  
  56.     return 0;  
  57. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值