您的位置:首页 > 运维架构

【codeforces 675E】【DP+线段树维护】【给出n个车站,并告诉你第i个车站有从i+1到a[i]个车站的直达票,问你所有车站到其余各个车站的最小车票花费和】

2016-10-02 22:52 567 查看
传送门:E. Trains and Statistic

描述:

E. Trains and Statistic

time limit per test
2 seconds

memory limit per test
256 megabytes

input
standard input

output
standard 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.

题意:

给出n个车站,并告诉你第i个车站有从i+1到a[i]个车站的直达票,问你所有车站到其余各个车站的最小车票花费和。

思路:
我们用dp[i]表示第i个车站从i+1到n最小车票花费总和。那么dp
=0,于是就可以从n到1逆推,求和计算出答案。

我们再来看如何得到dp[i]。对于从i+1到a[i]的车站,从第i号车站各只需要一张车票即可。但是对于大于a[i]的车站,我们可以找到从i+1到a[i]的a值最大的车站m。因为a[m]最大,所以能到达的范围也就更大,所以从这个m车站到剩下的车站花费车票一定最少的。这也就是我们贪心的依据。

找这个m的时候可以先用线段树求出区间里最大的a值然后用set二分查询一下m

这时候我们发现从m+1到a[i]在dp[m]中已经计算过了,各多算了一次,所以结果要减去a[i]-m,

算得dp[i]=dp[m]+(n-i)-(a[i]-m),

再将所有的dp[i]加起来就好了

线段树专题参考Here

代码:
#include <bits/stdc++.h>
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define pr(x) cout << #x << "= " << x << " " ;
#define pl(x) cout << #x << "= " << x << endl;
#define ll __int64
using namespace std;

#define mst(ss,b) memset(ss,b,sizeof(ss));
#define For(i,j,n) for(int i=j;i<=n;i++)

template<class T> void read(T&num) {
char CH; bool F=false;
for(CH=getchar();CH<'0'||CH>'9';F= CH=='-',CH=getchar());
for(num=0;CH>='0'&&CH<='9';num=num*10+CH-'0',CH=getchar());
F && (num=-num);
}
int stk[70], tp;
template<class T> inline void print(T p) {
if(!p) { puts("0"); return; }
while(p) stk[++ tp] = p%10, p/=10;
while(tp) putchar(stk[tp--] + '0');
putchar('\n');
}

const int N=1e5+10;

ll dp
,ans=0;
int Max[N<<2],a
,n;
set<int>s
;

void PushUp(int rt){
Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
}

void build(int l,int r,int rt){//线段树求区间最大值
if(l==r){
Max[rt]=a[l];
return;
}
int m=(r+l)>>1;
build(lson);
build(rson);
PushUp(rt);
}

int query(int L,int R, int l,int r,int rt){
if(L<=l && r<=R)return Max[rt];
int m=(l+r)>>1;
int ret=0;
if(L<=m)ret=max(ret, query(L, R, lson));
if(R>m)ret=max(ret,query(L, R, rson));
return ret;
}

int main(){
read(n);
s[0].insert(n);
for(int i=1; i<n; i++){
read(a[i]);
s[a[i]].insert(i);
}
build(1, n, 1);
dp
=0;
for(int i=n-1; i>=1; i--){
int m=query(i+1, a[i], 1, n, 1);//求i+1——a[i]车站之间某车站m使得a[m]最大
m=*s[m].lower_bound(i+1);//返回大于等于i+1中第一个出现a[m]的下标
dp[i]=dp[m]+n-i-(a[i]-m);
ans+=dp[i];
}
print(ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐