习题:Trains and Statistic(DP)

题目

传送门

思路

我们考虑一种贪心策略

如果从u不能一步到达v,那么一定存在一种方案使得u的第一步是达到\(u+1到p_u\)中最大的一个\(p_i\)的i位置

也就是指我们可以考虑从后往前扫,\(dp_i\)表示以i号节点为起始节点的总和

之后考虑i号节点到其他节点都是要多走一步,但是对于\(j到a_i\)的位置却夺走了一步,所以还要减去

\(dp_i=dp_j+(n-i)+(a_i-j)\)

区间最大值直接线段树不香?

虽然可以直接用st表

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define pii pair<int,int>
#define x first
#define y second 
namespace lst
{
	long long a[100005];
	struct node
	{
		int l,r;
		int val,ans;
	}tre[400005];
	void build(int l,int r,int k)
	{
		tre[k].l=l;
		tre[k].r=r;
		if(l==r)
		{
			tre[k].val=a[l];
			tre[k].ans=l;
			return;
		}
		int mid=(l+r)>>1;
		build(l,mid,k<<1);
		build(mid+1,r,k<<1|1);
		if(tre[k<<1].val>tre[k<<1|1].val)
			tre[k].ans=tre[k<<1].ans;
		else
			tre[k].ans=tre[k<<1|1].ans;
		tre[k].val=max(tre[k<<1].val,tre[k<<1|1].val);
	}
	pii ask(int l,int r,int k)
	{
		if(l>tre[k].r||tre[k].l>r)
			return make_pair(0,0);
		if(l<=tre[k].l&&tre[k].r<=r)
			return make_pair(tre[k].ans,tre[k].val);
		pii t1=ask(l,r,k<<1),t2=ask(l,r,k<<1|1);
		if(t1.y>t2.y)
			return t1;
		else
			return t2;
	}	
}
using namespace lst;
int n;
long long ans;
long long dp[100005];
int main()
{
	cin>>n;
	for(int i=1;i<n;i++)
		cin>>a[i];
	build(1,n-1,1);
	dp[n]=0;
	for(int i=n-1;i>=1;i--)
	{
		
		if(a[i]>=n)
			dp[i]=n-i;
		else
		{
			pii t1=ask(i+1,a[i],1);
        	//printf("When i == %d, k == %d\n",i,t1.x);
			dp[i]=dp[t1.x]+(n-i)-(a[i]-t1.x);
		}
		//printf("f[%d] == %lld\n",i,dp[i]);
		ans=ans+dp[i];
	}
	cout<<ans;
	return 0;
}
posted @ 2020-08-24 16:13  loney_s  阅读(113)  评论(0)    收藏  举报