bzoj 1691: [Usaco2007 Dec]挑剔的美食家(splay)

1691: [Usaco2007 Dec]挑剔的美食家

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 670   Solved: 308
[ Submit][ Status][ Discuss]

Description

与很多奶牛一样,Farmer John那群养尊处优的奶牛们对食物越来越挑剔,随便拿堆草就能打发她们午饭的日子自然是一去不返了。现在,Farmer John不得不去牧草专供商那里购买大量美味多汁的牧草,来满足他那N(1 <= N <= 100,000)头挑剔的奶牛。 所有奶牛都对FJ提出了她对牧草的要求:第i头奶牛要求她的食物每份的价钱不低于A_i(1 <= A_i <= 1,000,000,000),并且鲜嫩程度不能低于B_i(1 <= B_i <= 1,000,000,000)。商店里供应M(1 <= M <= 100,000)种不同的牧草,第i 种牧草的定价为C_i(1 <= C_i <= 1,000,000,000),鲜嫩程度为D_i (1 <= D_i <= 1,000,000,000)。 为了显示她们的与众不同,每头奶牛都要求她的食物是独一无二的,也就是说,没有哪两头奶牛会选择同一种食物。 Farmer John想知道,为了让所有奶牛满意,他最少得在购买食物上花多少钱。

Input

* 第1行: 2个用空格隔开的整数:N 和 M

* 第2..N+1行: 第i+1行包含2个用空格隔开的整数:A_i、B_i * 第N+2..N+M+1行: 第j+N+1行包含2个用空格隔开的整数:C_i、D_i

Output

* 第1行: 输出1个整数,表示使所有奶牛满意的最小花费。如果无论如何都无法 满足所有奶牛的需求,输出-1

Sample Input

4 7
1 1
2 3
1 4
4 2
3 2
2 1
4 3
5 2
5 4
2 6
4 4

Sample Output

12

输出说明:

给奶牛1吃价钱为2的2号牧草,奶牛2吃价钱为4的3号牧草,奶牛3分到价钱
为2的6号牧草,奶牛4选择价钱为4的7号牧草,这种分配方案的总花费是12,为
所有方案中花费最少的。

HINT

Source

[ Submit][ Status][ Discuss]


题解:贪心+splay

把奶牛和牧草都按照价格从小到大排序。

如果奶牛的需求价格小于等于当前牧草的价格,那么就把奶牛所需牧草的新鲜度加入splay中,然后把所有满足条件的奶牛的新鲜度加入splay后,在把当前牧草的新鲜度加入splay,然后查找当前牧草的前驱,如果有就把前驱点的个数--,当期牧草的价格计入答案,再删去牧草的新鲜度,如果没有前驱点就直接删去牧草的新鲜度。如果最后splay中节点个数为0,则说明全部奶牛都匹配成功,输出答案,否则输出-1。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 200003
using namespace std;
int n,m;
struct data
{
	int c,num;
};data cow[N],grass[N];
int fa[N],ch[N][3],key[N],size[N],cnt[N],tot,root;
long long ans;
int cmp(data a,data b)
{
	return a.c<b.c||a.c==b.c&&a.num<b.num;
}
int get(int x)
{
	return ch[fa[x]][1]==x;
}
void clear(int x)
{
	size[x]=cnt[x]=key[x]=ch[x][1]=ch[x][0]=fa[x]=0;
}
void update(int x)
{
	size[x]=cnt[x];
	if (ch[x][0]) size[x]+=size[ch[x][0]];
	if (ch[x][1]) size[x]+=size[ch[x][1]];
}
void rotate(int x)
{
	int y=fa[x]; int z=fa[y];  int which=get(x);
	if (z)
	 ch[z][ch[z][1]==y]=x;
	ch[y][which]=ch[x][which^1]; fa[ch[y][which]]=y;
	fa[y]=x; fa[x]=z; ch[x][which^1]=y; 
	update(y); update(x);
}
void splay(int x)
{
	for (int f;(f=fa[x]);rotate(x))
	 if (fa[f])
	  rotate(get(x)==get(f)?f:x);
	root=x;
}
void insert(int x)
{
	if (!root)
	 {
	 	root=++tot; 
	 	clear(tot); size[tot]=cnt[tot]=1; key[tot]=x;
	 	return ;
	 }
	int now=root; int f=0;
	while (true)
	{
		if (x==key[now])
		 {
		 	cnt[now]++;
		 	update(now); update(f); splay(now);
		 	return;
		 }
		f=now;
		now=ch[now][key[now]<x];
		if (!now)
		 {
		 	tot++; clear(tot);
		 	cnt[tot]=size[tot]=1; key[tot]=x;
		 	fa[tot]=f; ch[f][key[f]<x]=tot;
		 	update(f); splay(tot); return; 
		 }
	}
}
int find(int x)
{
	int now=root; int ans=0;
	while (true)
	{
		if (x==key[now])
		 return now;
		if (x<key[now])
		 now=ch[now][0];
		else
		 now=ch[now][1];
	}
}
int pre()
{
	if (cnt[root]>1) return root;
	int now=ch[root][0];
	while (ch[now][1]) now=ch[now][1];
	return now;
}
void del(int x)
{
	splay(x);
	if (cnt[root]>1)
     {
     	cnt[root]--;
     	update(root); splay(root);
     	return ;
     }
    if (!ch[root][0]&&!ch[root][1]) 
	{
	   clear(root);
	   root=0;
	   return;
	}
    if (!ch[root][1])
    {
    	int old=root; root=ch[root][0]; fa[root]=0; clear(old); 
    	return;
    }
    if (!ch[root][0])
    {
    	int old=root; root=ch[root][1]; fa[root]=0; clear(old); 
    	return ;
    }
    int k=pre(); int old=root; splay(k);
    ch[k][1]=ch[old][1]; fa[ch[old][1]]=k;
    clear(old);
    update(k);
    return ;
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	 scanf("%d%d",&cow[i].c,&cow[i].num);
	for (int i=1;i<=m;i++)
	 scanf("%d%d",&grass[i].c,&grass[i].num);
	sort(cow+1,cow+n+1,cmp);
	sort(grass+1,grass+m+1,cmp);
	int j=1;
	for (int i=1;i<=m;i++)
	 {
	 	while (cow[j].c<=grass[i].c&&j<=n)
	 	 insert(cow[j].num),j++;
	 	insert(grass[i].num);
	 	int k=pre();
	 	if (k)
	 	 {
	 	 	ans=(long long) ans+grass[i].c;
	 	 	del(k);
	 	 }
	 	k=find(grass[i].num);
	 	del(k);
	 }
//	cout<<ans<<endl;
	if (size[root]==0&&j>n)
	 printf("%lld\n",ans);
	else
	 printf("-1\n");
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值