【BZOJ 1691】[Usaco2007 Dec]挑剔的美食家 treap+贪心

236 篇文章 0 订阅
38 篇文章 0 订阅

首先对于所有的奶牛和草堆按照鲜嫩度排序,这样倒着枚举奶牛,动态插入所有大于等于当前美味度的草堆,这样在treap中维护的所有的节点都是合法的,然后就可以贪心的去找后继了。贪心的证明比较简单就不说了

BZOJ上不能使用ctime函数库,但是不初始化种子的话特别容易挂,所以随便相乘取一下质数的mod就好了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#define LL long long
#define maxn 100020
#include<algorithm>
using namespace std;
int n,m;LL ans;
typedef pair<int,int>pii;
pii gra[maxn],cow[maxn];
struct Tree{
	int r,x,size;
	Tree* ch[2];
	Tree(int a,int b,Tree* c=NULL,Tree* d=NULL){
		r=a,x=b,ch[0]=c,ch[1]=d,size=1;
	}
};
Tree* rt=NULL;//0 左 1 右 
void Rotate(Tree* &u,int d){
	Tree* v=u->ch[!d];u->ch[!d]=v->ch[d],v->ch[d]=u;
	u=v;
}
void insert(Tree* &u,int x){
	if(u==NULL){
		u=new Tree(rand(),x,NULL,NULL);
		return;
	}
	if(u->x==x){
		u->size++;return;
	}int d;
	if(x>u->x){
		insert(u->ch[d=1],x);
	}else insert(u->ch[d=0],x);
	if(u->ch[d]->r > u->r)Rotate(u,!d);
}
int last;
int find(Tree* u,int x){
	if(u==NULL)return last;
	if(u->x==x)return x;
	int d=x>u->x;
	if(d==1)return find(u->ch[d],x);
	else {
		last=u->x;
		return find(u->ch[d],x);
	}
}
void remove(Tree* &u,int x){
	if(u->x==x){
		if(u->size>1){u->size--;return;}
		if(u->ch[0]==NULL)u=u->ch[1];
		else if(u->ch[1]==NULL)u=u->ch[0];
		else{
			if(u->ch[0]->r>u->ch[1]->r){
				Rotate(u,1);remove(u->ch[1],x);
			}else {
				Rotate(u,0);remove(u->ch[0],x);
			}
		}
	}else remove(u->ch[(x>u->x)],x);
}
void solve(){
	int r=m;
	for(int x,c,d,i=n;i>=1;i--){
		d=cow[i].first,c=cow[i].second;
		while(r>0&&d<=gra[r].first)insert(rt,gra[r].second),r--;
		if(rt==NULL){puts("-1");return;}
		x=find(rt,c);
		if(x==-1){puts("-1");return;}
		ans+=(LL)x;
		remove(rt,x);
	}
	printf("%lld",ans);
}
int main(){
	scanf("%d%d",&n,&m);
	srand(n%2333*(m%2333)%2333);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&cow[i].second,&cow[i].first);
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d",&gra[i].second,&gra[i].first);
	}
	sort(cow+1,cow+1+n);sort(gra+1,gra+1+m);
	solve();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值