P3575-[POI2014]DOO-Around the world【环形dp】

版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/89575493

正题

题目大意:https://www.luogu.org/problemnew/show/P3575


题目大意

一个环,上面有若干个点,若干个询问 x x
表示上一个降落点和下一个降落点距离不能超过 x x ,然后求至少要降落多少次(起点可以任意)


解题思路

首先明显环形 d p dp ,然后断环成链,复制一份放到后面,预处理距离前缀和数组 s u m sum
然后考虑贪心设 l a s t i last_i 数组,定义 d p i dp_i 表示 l a s t i i ( i > n ) last_i\sim i(i>n) 这段范围的降落次数,然后优先飞最远。也就是 j j i i 的最前的降落地点。因为 j j 满足单调性,所以就可以 O ( n ) O(n) 的预处理出来(或者在 d p dp 的时候处理)。然后首先显然
d p i = d p j + 1 dp_i=dp_j+1
然后因为计算上了 d p j dp_j ,所以要从 l a s t j last_j 开始也就是
l a s t i = l a s t j last_i=last_j
然后当 i l a s t i > = n i-last_i>=n 时就是答案了。


c o d e code

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2000100;
ll n,t,imp;
ll sum[N],dp[N],last[N];
void get_answer(ll l){
	if(l<imp){
		printf("NIE\n");
		return;
	}
	for(ll i=n+1,j=1;i<=2*n;i++){
		while(sum[i]-sum[j]>l) j++;
		dp[i]=dp[j]+1;
		last[i]=last[j];
		if(i-last[i]>=n)
		{
			printf("%lld\n",dp[i]);
			return;
		}
	}
}
int main()
{
	scanf("%lld%lld",&n,&t);
	for(ll i=1;i<=n;i++){
		last[i]=i;
		scanf("%lld",&sum[i]);
		imp=max(imp,sum[i]);
		sum[i+n]=sum[i];
		sum[i]+=sum[i-1];
	}
	for(ll i=n+1;i<=2*n;i++)
	  sum[i]+=sum[i-1];
	for(ll i=1;i<=t;i++){
		ll l;scanf("%lld",&l);
		get_answer(l);
	}
} 

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/89575493