洛谷P2073 送花_Treap

本文介绍了一种使用平衡树解决花束选择问题的方法,通过Treap或Splay实现在一系列添加和删除操作后计算花束总价值及美观度。文章记录了作者四天的调试经历,并分享了最终通过的代码。

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

题目背景

小明准备给小红送一束花,以表达他对小红的爱意。他在花店看中了一些花,准备用它们包成花束。

题目描述

这些花都很漂亮,每朵花有一个美丽值W,价格为C。

小明一开始有一个空的花束,他不断地向里面添加花。他有以下几种操作:

操作 含义

1 W C 添加一朵美丽值为W,价格为C的花。

3 小明觉得当前花束中最便宜的一朵花太廉价,不适合送给小红,所以删除最便宜的一朵花。

2 小明觉得当前花束中最贵的一朵花太贵,他心疼自己的钱,所以删除最贵的一朵花。

-1 完成添加与删除,开始包装花束

若删除操作时没有花,则跳过删除操作。

如果加入的花朵价格已经与花束中已有花朵价格重复,则这一朵花不能加入花束。

请你帮小明写一个程序,计算出开始包装花束时,花束中所有花的美丽值的总和,以及小明需要为花束付出的总价格。

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1: 
1 1 1
1 2 5
2
1 3 3
3
1 5 2
-1
输出样例#1: 
8 5

说明

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

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

经典 平衡树 问题,用 Treap 或 Splay 都可以。
但是我做这题用了4天。。。
第一天:(RE 0)*4
第二天:(RE 0)*2+(RE 60)*2
第三天:(WA 0)*2+(WA 40)*3
第四天:(WA 0)*1+(WA 40)*1+(AC 100)*1
实在是丧心病狂的题目。。。
注意:不要将 操作2 与 操作3 弄反,题目有毒。。。
附上苦心敲出的AC代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct node{
	node* son[2];
	int v,data,sum1,sum2,w;
	node(){
		son[0]=son[1]=NULL;
		w=rand();
		v=data=sum1=sum2=0;
	}
};
node* rt;
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
void maintain(node* &u){
	u->sum1=u->v;u->sum2=u->data;
	if(u->son[0]!=NULL){
		u->sum1+=u->son[0]->sum1;
		u->sum2+=u->son[0]->sum2;
	}
	if(u->son[1]!=NULL){
		u->sum1+=u->son[1]->sum1;
		u->sum2+=u->son[1]->sum2;
	}
}
void turn(node* &u,int f){
	node* t=u->son[f^1];
	u->son[f^1]=t->son[f];
	t->son[f]=u;
	maintain(u);
	maintain(t);
	u=t;
}
bool insert(node* &u,int w,int c){
	if(u==NULL){
		u=new node;
		u->v=c;u->data=w;
		maintain(u);
		return true;
	}
	else if(u->v==c)return false;
	int y=c>u->v?1:0;
	bool f=insert(u->son[y],w,c);
	if(u->son[y]->w>u->w)turn(u,y^1);
	if(u!=NULL)maintain(u);
	return f;
}
void remove(node* &u,int x,int y){
	if(u==NULL)return;
	if(u->v==x){
		if(u->son[0]==NULL&&u->son[1]==NULL)u=NULL;
		else if(u->son[0]!=NULL&&u->son[1]!=NULL){
			if(u->son[0]->w>u->son[1]->w){
				turn(u,1);
				remove(u->son[1],x,y);
			}
			else{
				turn(u,0);
				remove(u->son[0],x,y);
			}
		}
		else{
			if(u->son[0]==NULL)u=u->son[1];
			else u=u->son[0];
		}
		if(u!=NULL)maintain(u);
		return;
	}
	u->sum1-=x;u->sum2-=y;
	if(x<u->v)remove(u->son[0],x,y);
	else if(x>u->v)remove(u->son[1],x,y);
	if(u!=NULL)maintain(u);
}
void find(node* u,int k,int &x,int &y){
	if(k)while(u->son[0]!=NULL)u=u->son[0];
	else while(u->son[1]!=NULL)u=u->son[1];
	x=u->v;y=u->data;
}
int main(){
	srand(987);
	int x,y,z,m=0;
	while(1){
		scanf("%d",&x);
		if(x==-1)break;
		if(x==1){
			scanf("%d%d",&y,&z);
			if(insert(rt,y,z))m++;
			continue;
		}
		if(m==0)continue;
		m--;
		find(rt,(x==3?1:0),y,z);
		remove(rt,y,z);
	}
	if(rt==NULL)printf("0 0\n");
	else printf("%d %d\n",rt->sum2,rt->sum1);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值