hdu 3016 Man Down(成段更新,单点查询)

解决一个涉及木板跳跃的问题,通过动态规划与线段树优化算法寻找从最高木板跳到地面并获得最大生命值的路径。

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

题意:有N块木板,每块木板有四个属性,高h(h>0),左边界xl,右边界xr(0<xl<xr<100000),以及掉落在它上面,获得多少生命值value(-1000<value<1000),一个人从最高的木板开始往下跳,初始时生命值为100,问最后掉落到地面能获得的生命值最多为多少(如果生命值为0,那么这个人会死去),如果无法跳到地面,输出-1。

这是一个简单的DP或者说最长路。但是木板的数目很大,如果建立每个木板到其他木板的转移用O(N^2)的复杂度,显然是会超时的,这时候,就可以借助线段树。将木板按h从小到大排序,初始时,更新线段树里的全部节点为地面的下标,然后往上添加木板的过程就是,单点查询每个木板的xl和xr,得到的下标就是当前木板能转移到的其他木板(或者地面),然后将这段区间更新为当前木板的下标。

最后跑一个dp或者最长路都可以。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;

#define LL(x) (x<<1)
#define RR(x) (x<<1|1)
#define MID(a,b) (a+((b-a)>>1));
#define INF (1<<30)
const int N=100005;

struct BOARD
{
	int h,x1,x2,valu,ind;
	BOARD(){}
	BOARD(int a,int b,int c,int d)
	{h=a;x1=b;x2=c;valu=d;}
	bool operator<(const BOARD&b)const
	{ return h<b.h; }
}p[N];
struct node
{
	int lft,rht,flag;
	int mid(){return MID(lft,rht);}
};

int n,dis[N];
vector<int> y;
vector<pair<int,int> >adj[N];
map<int,int> H;
bool vis[N];

struct Segtree
{
	node tree[N*4];
	void down(int ind)
	{
		if(tree[ind].flag)
		{
			tree[LL(ind)].flag=tree[ind].flag;
			tree[RR(ind)].flag=tree[ind].flag;
			tree[ind].flag=0;
		}
	}
	void build(int lft,int rht,int ind)
	{
		tree[ind].lft=lft;	tree[ind].rht=rht;
		tree[ind].flag=0;
		if(lft!=rht)
		{
			int mid=tree[ind].mid();
			build(lft,mid,LL(ind));
			build(mid+1,rht,RR(ind));
		}
	}
	void updata(int st,int ed,int ind,int valu)
	{
		int lft=tree[ind].lft,rht=tree[ind].rht;
		if(st<=lft&&rht<=ed) tree[ind].flag=valu;
		else
		{
			down(ind);
			int mid=tree[ind].mid();
			if(st<=mid) updata(st,ed,LL(ind),valu);
			if(ed> mid) updata(st,ed,RR(ind),valu);
		}
	}
	int query(int pos,int ind)
	{
		if(tree[ind].flag) return tree[ind].flag;
		else
		{
			int mid=tree[ind].mid();
			if(pos<=mid) return query(pos,LL(ind));
			else return query(pos,RR(ind));
		}
	}
}seg;
int spfa(int st,int ed,int valu)
{
	if(valu<=0) return -1;
	queue<int> que;
	dis[st]=valu;
	que.push(st);
	while(!que.empty())
	{
		int u=que.front(); que.pop();
		vis[u]=0;
		for(int i=0;i<(int)adj[u].size();i++)
		{
			int v=adj[u][i].first,w=adj[u][i].second;
			if(dis[u]+w<=0) continue;
			if(dis[v]<dis[u]+w)
			{
				dis[v]=dis[u]+w;
				if(!vis[v])
				{
					que.push(v);
					vis[v]=1;
				}
			}
		}
	}
	//for(int i=ed;i<=st;i++) printf("%d ",dis[i]);puts("");
	if(dis[ed]<=0) return -1;
	return dis[ed];
}
int main()
{
	//freopen("in.txt","r",stdin);
	while(scanf("%d",&n)!=EOF)
	{
		y.clear(); H.clear();
		for(int i=0;i<=n+5;i++)
		{
			adj[i].clear(),dis[i]=-INF,vis[i]=0;
		}

		int h,x1,x2,valu;
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d%d%d",&h,&x1,&x2,&valu);
			p[i]=BOARD(h,x1,x2,valu);
			y.push_back(x1); y.push_back(x2);
		}
		p[++n]=BOARD(0,0,N+5,0);
		y.push_back(0); y.push_back(N+5);
		sort(p+1,p+n+1);
		for(int i=1;i<=n;i++)
		{
			p[i].ind=i;
			//cout<<p[i].h<<" "<<p[i].x1<<" "<<p[i].x2<<endl;
		}

		sort(y.begin(),y.end());
		y.erase(unique(y.begin(),y.end()),y.end());
		for(int i=0;i<(int)y.size();i++) H[y[i]]=i;
		seg.build(0,(int)y.size()-1,1);

		seg.updata(H[p[1].x1],H[p[1].x2],1,p[1].ind);
		for(int i=2;i<=n;i++)
		{
			int tmp1=seg.query(H[p[i].x1],1);
			adj[p[i].ind].push_back(make_pair(tmp1,p[tmp1].valu));
			//cout<<p[i].ind<<"->"<<tmp1<<" "<<p[tmp1].valu<<endl;
			int tmp2=seg.query(H[p[i].x2],1);
            if(tmp1!=tmp2)
                adj[p[i].ind].push_back(make_pair(tmp2,p[tmp2].valu));
			//cout<<p[i].ind<<"->"<<tmp2<<" "<<p[tmp2].valu<<endl;
			seg.updata(H[p[i].x1],H[p[i].x2],1,p[i].ind);
		}
		int res=spfa(p[n].ind,p[1].ind,p[n].valu+100);
		printf("%d\n",res);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值