HDU2836(线段树和二分法+dp)

本文介绍了一个有趣的编程问题:如何计算在特定条件下,从一系列不同高度的盒子中选择并跳过湖面的可能性数量。通过使用线段树和二分查找方法,优化了动态规划算法,以解决大规模数据输入的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Traversal

Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 599    Accepted Submission(s): 206


Problem Description
I arrive at a big lake and I have to cross it. Luckily, I’m a very good jumper, but the lake is too big to be crossed in one jump. On the shore, I find N boxes of different heights, set in a certain order. If I throw a box into the lake, it will float and it will have the same height as on the shore. This is good, because I intend to throw some boxes into the lake and get from one shore to the other by jumping from box to box. The only things to consider are:
The lake is big, so I must throw at least 2 boxes, which means that in order to cross the lake I have to make at least 3 jumps.
Not all the boxes have to be thrown; some of them may be ignored.
The boxes can be thrown into the lake only in the order they are found on the shore and I have to jump on them in this order.
The height difference between two consecutive boxes I use must be at most H meters, because I can jump a lot in length, but I have some problems with jumping in height.The height of a box doesn’t change when I jump on it.
I’m always able to jump from the shore to a box and from a box to the shore, no matter what the height of the box is.

Facing so many possibilities that respect the above conditions, I begin counting the number of possibilities that I have, instead of actually crossing the lake. I quickly find the answer and I wonder whether you can also find it as fast as I did.

Task

Write a program that determines the number of possibilities to cross the lake in the above conditions. Since the number can be quite big, you only have to output the remainder of this number, when divided by 9901.
 

Input
There are multiple test cases. Each test case contains two integers N and H, separated by a space, representing the number of boxes and the maximum height difference between two consecutive boxes thrown into the lake. The following N lines contain the heights of the boxes, in the order the boxes are set on the shore. The (i+1)th line contains the height of the ith box.
 

Output
For each test case you should output a single line, containing the number of possibilities modulo 9901.

Constraints

1 < N < 100 001
0 < H < 100 000 001
The height of any box is a strictly positive integer and does not exceed 100 000 000
 

Sample Input
4 2 1 3 7 5
 

Sample Output
4

Hint
Explanation There are 4 possibilities: 1 3 1 3 5 3 5 7 5
 
线段树和二分法+dp的有效利用,状态转移方程很简单dp[i]=sum(dp[j])+1,(0<j<i,dp[i]表示前i个数可以的组合),但由于n特别大,直接动态规划会超时,因此尝试二分法与线段树来有效减少dp[i]的求解,整个算法时间复杂度为O(n*log(n
#include<iostream> 
#include<algorithm>
using namespace std;
const int mod=9901;
const int maxn=100005;

int max(int a,int b)
{
	return a<b?b:a;
}

int min(int a,int b)
{
	return a<b?a:b;
}

typedef struct node
{
	int l,r,c;//c代表是区间[l,r]的dp状态和 
}node;

typedef struct segment
{
	node tree[4*maxn]; 
	void build(int k,int l,int r); 
	void add(int k,int i,int x); 
	int query(int k,int l,int r);
}segment;
segment s;

void segment::build(int k,int l,int r)
{ 
	tree[k].l=l;
	tree[k].r=r;
	tree[k].c=0; 
	if(l==r)
		return ; 
	int mid=(l+r)>>1; 
	build(k*2,l,mid);
	build(k*2+1,mid+1,r);
} 

void segment::add(int k,int i,int x)
{ 
	tree[k].c=(tree[k].c+x)%mod; 
	if(tree[k].l==tree[k].r)
		return ; 
	int mid=(tree[k].l+tree[k].r)>>1; 
	if(i<=mid)
		add(k*2,i,x); 
	else 
		add(k*2+1,i,x); 
} 

int segment::query(int k,int l,int r)
{ 
	if(tree[k].l==l&&tree[k].r==r)
		return tree[k].c;
	int mid=(tree[k].l+tree[k].r)>>1,ans=0; 
	if(r<=mid)
		ans+=query(k*2,l,r); 
	else if(l>mid)
		ans+=query(k*2+1,l,r); 
	else
	{
		ans+=query(k*2,l,mid); 
		ans+=query(k*2+1,mid+1,r); 
	}
	return ans%mod;
}

int xx[maxn],len;
int left_bound(int key)
//查左界 找到最小的mid使得xx[mid]>=key 
{ 
	int left=0,right=len-1,mid,ans=len-1; 
	while(left<=right) 
	{ 
		mid=(left+right)>>1; 
		if(xx[mid]>=key)
		{
			ans=mid;
			right=mid-1;
		} 
		else 
			left=mid+1;
	} 
	return ans; 
} 

int right_bound(int key)
//查右界找到最大的mid使得xx[mid]<=key 
{ 
	int left=0,right=len-1,mid,ans=0; 
	while(left<=right) 
	{
		mid=(left+right)>>1; 
		if(xx[mid]<=key)
		{
			ans=mid;
			left=mid+1;
		} 
		else
			right=mid-1; 
	} 
	return ans; 
} 

int find(int key)
//精确查找 
{ 
	int left=0,right=len-1,mid; 
	while(left<=right)
	{
		mid=(left+right)>>1;
		if(key==xx[mid])
			return mid;
		else if(key>xx[mid])
			left=mid+1; 
		else 
			right=mid-1;
	} 
} 

int a[maxn],dp[maxn]; 
int main()
{ 
	int i,j,k,n,h,l,r,sum;
	while(scanf("%d%d",&n,&h)!=EOF) 
	{ 
		len=0; 
		for(i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			xx[len++]=a[i];
		} 
		sort(xx,xx+n); 

		s.build(1,0,len-1); 

		//顺序查找a[i],可保证后面的不影响前面的排列
		for(i=1;i<=n;i++) 
		{ 
			//用二分法有效缩短每次的查询时间
			l=left_bound(max(xx[0],a[i]-h));
			r=right_bound(min(xx[len-1],a[i]+h));

			//用线段树用小缩短每次的查询时间
			dp[i]=s.query(1,l,r); 
			k=find(a[i]); 
			s.add(1,k,dp[i]+1);
		} 

		sum=0; 
		for(i=2;i<=n;i++)
		{
	//		cout<<dp[i]<<" " ;
			sum=(sum+dp[i])%mod;
		}
		printf("%d\n",sum); 
	} 
	return 0;
} 

))
 
 
 
 
 
 
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值