codeforces 675E Trains and Statistic 线段树+贪心统计

分析:这个题刚看起来无从下手

但是我们可以先简化问题,首先可以固定起点i,求出i+1到n的最小距离

它可以到达的范围是[i+1,a[i]],贪心的想,我们希望换一次车可以到达的距离尽量远

即:找一个k,使得i+1<=k<=a[i],a[k]的值最大,就可以保证,换一次车,可以到达的距离最

找k的操作可以用线段树来完成

统计当前dp[i]=dp[k]+(n-i)-(a[i]-k),因为当前区间内的点在[k+1,a[i]]的点多计了一次,所以减去

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=1e5+5;
int c[N<<2],a[N];
LL dp[N];
void up(int rt){
  if(a[c[rt<<1]]>=a[c[rt<<1|1]])
    c[rt]=c[rt<<1];
  else c[rt]=c[rt<<1|1];
}
void build(int rt,int l,int r){
  if(l==r){c[rt]=l;return;}
  int m=(l+r)>>1;
  build(rt<<1,l,m);
  build(rt<<1|1,m+1,r);
  up(rt);
}
int ask(int rt,int l,int r,int x,int y){
  if(x<=l&&r<=y)return c[rt];
  int ls=-1,rs=-1,m=(l+r)>>1;
  if(x<=m)ls=ask(rt<<1,l,m,x,y);
  if(y>m)rs=ask(rt<<1|1,m+1,r,x,y);
  if(ls==-1)return rs;
  if(rs==-1)return ls;
  if(a[ls]>=a[rs])return ls;
  else return rs;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<n;++i)
      scanf("%d",&a[i]);
    a[n]=n;
    LL ret=0;
    build(1,1,n);
    for(int i=n-1;i>0;--i){
       int pos=ask(1,1,n,i+1,a[i]);
       dp[i]=dp[pos]+(n-i)-(a[i]-pos);
       ret+=dp[i];
    }
    printf("%I64d
",ret);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/shuguangzw/p/5535192.html