P2073 送花(权值线段树)

本文介绍了一种使用权值线段树的数据结构解决特定问题的方法,通过维护花束中花朵的价格和美丽值,实现了在一系列添加和删除操作后的花束总价值快速计算。
题目描述

小明准备给小红送一束花,以表达他对小红的爱意。他在花店看中了一些花,准备用它们包成花束。
这些花都很漂亮,每朵花有一个美丽值W,价格为C。
小明一开始有一个空的花束,他不断地向里面添加花。他有以下几种操作:
操作 含义
1 W C 添加一朵美丽值为W,价格为C的花。
3 小明觉得当前花束中最便宜的一朵花太廉价,不适合送给小红,所以删除最便宜的一朵花。
2 小明觉得当前花束中最贵的一朵花太贵,他心疼自己的钱,所以删除最贵的一朵花。
-1 完成添加与删除,开始包装花束
若删除操作时没有花,则跳过删除操作。
如果加入的花朵价格已经与花束中已有花朵价格重复,则这一朵花不能加入花束。
请你帮小明写一个程序,计算出开始包装花束时,花束中所有花的美丽值的总和,以及小明需要为花束付出的总价格。

输入格式

若干行,每行一个操作,以-1结束。

输出格式

一行,两个空格隔开的正整数表示开始包装花束时,花束中所有花的美丽值的总和。以及小明需要为花束付出的总价格。

样例

输入
1 1 1
1 2 5
2
1 3 3
3
1 5 2
-1
输出
8 5

说明/提示

对于20%数据,操作数<=100,1<=W,C<=1000。
对于全部数据,操作数<=100000,1<=W,C<=1000000。

题目分析

这道题我们可以以价格为区间来维护一个权值线段树,权值线段树的节点中包含美丽值的区间和、价格的区间和

操作有四种:
1:向线段树中加入一朵美丽值为w,价格为c的花。用update函数,将这朵花直接放入即可。
2/3:删除最贵/最便宜的花。因为权值线段树是根据价格的大小放入不同的位置,因此我们进行删除操作时不能用update函数了,要重新写一个remove函数来完成操作。
-1:输出总的美丽值和价格。即为tr[1]中所包含的信息,直接输出即可。

代码如下
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#include <iomanip>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int N=1e6+5;
struct Node{
	int p,w;	//p记录价格的区间和,w记录美丽值的区间和
}tr[N*4];
void pushup(int u)
{
	tr[u].p=tr[u<<1].p+tr[u<<1|1].p;	//左右两子段的区间和相加即可得到父段的信息
	tr[u].w=tr[u<<1].w+tr[u<<1|1].w;
}
void update(int u,int l,int r,int x,int v)	//插入一朵价格为x,美丽值为v的花
{
	if(l==r)
	{
		if(tr[u].p==x) return;		//如果已经有一朵价格为x的花了,则不能加入该花
		tr[u].p=x;					//否则跟新该该点信息
		tr[u].w=v;
	}
	else
	{
		int mid=l+r>>1;				//二分查找x的位置
		if(x<=mid) update(u<<1,l,mid,x,v);
		else update(u<<1|1,mid+1,r,x,v);
		pushup(u);
	}
}
void remove(int u,int l,int r,int s)	//删除一朵花
{
	if(l==r) tr[u]={0,0};				//找到该点则删除信息
	else
	{
		int mid=l+r>>1;
		if(s)							//s=1是删除最大值
		{
			if(!tr[u<<1|1].p) remove(u<<1,l,mid,s);	//右区间为空则找左区间
			else remove(u<<1|1,mid+1,r,s);			//否则找右区间
		}
		else							//s=0删除最小值
		{
			if(!tr[u<<1].p) remove(u<<1|1,mid+1,r,s);	//同理
			else remove(u<<1,l,mid,s);
		}
		pushup(u);						//更新父节点信息
	}
}
int main()
{
    int op;
	while(scanf("%d",&op)!=EOF)
	{
		if(op==-1) break; 
		if(op==1)
		{
			int w,c;
			scanf("%d %d",&w,&c);
			update(1,1,1e6,c,w);
		}
		else if(tr[1].p==0) continue;		//如果花束为空则跳过该删除操作
		else if(op==2) remove(1,1,1e6,1);
		else remove(1,1,1e6,0);
	}
	printf("%d %d\n",tr[1].w,tr[1].p);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lwz_159

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值