牛客多校赛一 I题题解

I-Points Division

原题网站传送门:https://ac.nowcoder.com/acm/contest/881/I

题意:给你n个点,每个点有对应的整数坐标,将这些点划分成两个集合使得没有A集合的一个点在B集合的另一个点的右下方或是右方,下方。
题解:可以构造一个折线,使得所有折现左上角都是集合A中的点,右下角都是集合B中的点,不失一般性,我们可以直接假设折线上的所有节点都是集合B中的,这样的构造方法能够使得题意的条件得到满足。
考虑到所有点的y坐标范围特别大,我们先对y坐标进行离散化
dp[i]表示折现上x坐标对应的点为第i个节点时,所带来的最大收益。注意这个收益在插入新的节点后是实时更新的!
每次插入一个新节点后,他对之前节点影响为:
d p [ j ] = { d p [ j ] + b i if  j &lt; i , y j &gt; y i d p [ j ] + a i if  j &lt; i , y j &lt; y i dp[j] =\begin{cases} dp[j]+b_i &amp; \text{if }j&lt;i,y_j&gt;y_i \\ dp[j]+a_i &amp; \text{if }j&lt;i,y_j&lt;y_i \end{cases} dp[j]={dp[j]+bidp[j]+aiif j<i,yj>yiif j<i,yj<yi
他自身所对应的dp值为:
d p [ i ] = b i + max d p [ j ] ∣ 1 &lt; = j &lt; i , y j &lt; y i dp[i]=b_i+ \textbf{max}dp[j]|1&lt;=j&lt;i,y_j&lt;y_i dp[i]=bi+maxdp[j]1<=j<i,yj<yi
注意建立一个虚拟节点0,它的y坐标为0,这样把它放入b集合中,这样就能处理所有元素都在a集合的情况。

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <cmath>
#include <vector>
#define maxn 100005
typedef long long ll;
using namespace std;
struct segt
{
	int l,r;//y value
	ll mx,lazy;
}t[maxn*4];
void push_down(int p)
{
	if(t[p].lazy)
	{
		t[p<<1].mx+=t[p].lazy;
		t[p<<1|1].mx+=t[p].lazy;
		t[p<<1].lazy+=t[p].lazy;
		t[p<<1|1].lazy+=t[p].lazy;
		t[p].lazy=0;	
	}
} 
void push_up(int p)
{
	t[p].mx=max(t[p<<1].mx,t[p<<1|1].mx);
}
void build(int p,int l,int r)
{
	t[p].l=l,t[p].r=r;	
//	cout<<p<<endl;
	t[p].mx=t[p].lazy=0;
	if(l==r)return;
	
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
}
void add(int p,int l,int r,ll val,int flag)
{
	if(l>r)return;
	if(l<=t[p].l&&r>=t[p].r)
	{
		if(flag)t[p].mx=val;
		else
		{
			t[p].mx+=val;
			t[p].lazy+=val;
		}
		return;
	}
	push_down(p);
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid)add(p<<1,l,r,val,flag);
	if(r>mid)add(p<<1|1,l,r,val,flag);
	push_up(p);
}
ll query(int p,int l,int r)
{
	if(l>r)return 0;
	if(l<=t[p].l&&r>=t[p].r)return t[p].mx;
	push_down(p);
	ll mx=0;
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid)mx=max(mx,query(p<<1,l,r));
	if(r>mid)mx=max(mx,query(p<<1|1,l,r));
	return mx;
}
struct node
{
	ll x,y,a,b;
	bool operator <(const node &b)const
	{
		return x<b.x||x==b.x&&y>b.y;
	}
}p[maxn];
ll val[maxn];
int tot;
int main()
{
	int n;
	while(scanf("%d",&n)==1)
	{
		tot=n;
		for(int i=1;i<=n;i++)
		{
			scanf("%lld%lld%lld%lld",&p[i].x,&p[i].y,&p[i].a,&p[i].b);
			val[i]=p[i].y;
		}
		sort(val+1,val+1+tot);
		tot=unique(val+1,val+1+tot)-val-1;
		for(int i=1;i<=n;i++)
			p[i].y=lower_bound(val+1,val+1+tot,p[i].y)-val;
		sort(p+1,p+1+n);
		build(1,0,tot);
		for(int i=1;i<=n;i++)
		{
			add(1,p[i].y,p[i].y,query(1,0,p[i].y)+p[i].b,1);//查询到y 
			add(1,0,p[i].y-1,p[i].a,0);
			add(1,p[i].y+1,tot,p[i].b,0);//增加虚拟节点 
		//	cout<<query(1,1,tot)<<endl;
		}
		printf("%lld\n",t[1].mx);
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值