题意
给出一个长度为n的数组nx,表示从站台i出发可以做一班车走到[i+1,nx[i]],令dis(i,j)表示i到j最少坐几班车,求 ∑ni=1∑nj=i+1dis(i,j) ∑ i = 1 n ∑ j = i + 1 n d i s ( i , j )
分析
从后往前倒着推吧…然后用线段树优化一下。
然后还有个求和的过程。
…好像比想象的要麻烦。某段区间集体+1
不过还是很麻烦的。
要考虑到达某个点会不会造成影响之类的….?
先来考虑暴力的转移
dp[i]=min( dp[j]-(a[i]-(j+1)+1) +(n-(a[i]+1)+1) )+a[i]-(i+1)+1
(i+1<=j<=a[i])
然后化简一下就可以直接用线段树维护了。
结束。
一开始觉得很难想反而吓到自己了。其实只要抓住最暴力的dp就可以根据形态进行优化了。也算是一个小经验[?],当然很多时候通过性质优化才王道。
code
#include<bits/stdc++.h>
using namespace std;
void read(int &x){
x=0; char c=getchar();
for (;c<48;c=getchar());
for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
#define ll long long
#define M 100005
ll dp[M];
struct Tree{
#define ls (p<<1)
#define rs (p<<1|1)
ll a[M<<2];
void upd(int l,int r,int x,ll val,int p){
if (l==r){
a[p]=val;
return;
}
int mid=(l+r)>>1;
if (x<=mid)upd(l,mid,x,val,ls);
else upd(mid+1,r,x,val,rs);
a[p]=min(a[ls],a[rs]);
}
ll qu(int l,int r,int x,int y,int p){
if (l==x&&r==y)return a[p];
int mid=(l+r)>>1;
if (y<=mid)return qu(l,mid,x,y,ls);
if (x>mid)return qu(mid+1,r,x,y,rs);
return min(qu(l,mid,x,mid,ls),qu(mid+1,r,mid+1,y,rs));
}
}T;
int n,a[M];
int main(){
ll res=0;
read(n);
for (int i=1;i<n;i++)read(a[i]);
T.upd(1,n,n,2*n,1);
for (int i=n-1;i;i--){
dp[i]=T.qu(1,n,i+1,a[i],1);
dp[i]=dp[i]-a[i]-i;
res+=dp[i];
T.upd(1,n,i,dp[i]+i+n,1);
}
printf("%lld\n",res);
return 0;
}
总结
我居然把线段树打错了…某个max打成min了。傻掉了傻掉了,还调了很久…
关于常数
似乎可以通过把线段树换成倍增之类的方法来让常数变小。然而总的来说还是有点麻烦的… 我觉得46ms还可以啊[没上进心逃]