HDU 4268 - Alice and Bob

 

题目地址: http://acm.hdu.edu.cn/showproblem.php?pid=4268

 

贪心 + 树状数组

 

贪心是怪叔叔想到的,然后让我敲~~  = =

 

一开始敲搓了,搓B不解释了~~ 

 

贪心的想法很简单:

 

          首先:A和B混合排序,先按照L排,L相等的按照W排,W相等的B排A前面。

 

          然后,从头到尾依次遍历,假若遍历到B,则将B的W作为下标加入到树状数组中(当然W需要离散化,而树状数组tree[W]表示B的W的个数)。。。

 

          假若遍历到A,那么就去 [1,A的W] 这个区间内找是否存在有B的W,如果有,则将最大的B的W删除,当作被A覆盖。。。

 

那么树状数组的用途?

 

每次以O(2logn)的时间找到 [1,A的W] 中,最接近A的W的那个B的W。。。

 

那么怎么找?  首先求和,也就是 [1,A的W] 这个区间内 B的W 的个数num。。。

 

这时,我们利用树状数组求第num大的方法,得到一个W,那么这个W就是 [1,A的W] 这个区间中最接近 A的W 的那个 B的W 。。。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>

#define ll (v<<1)
#define rr (v<<1|1)
#define mid ((l+r)>>1)

using namespace std;

struct Node{
	int l,w,i;
}node[210000];

int cmpL(Node a,Node b){
	return a.l!=b.l?a.l<b.l:(a.w==b.w?a.i<b.i:a.w<b.w);
}

int cmpW(Node a,Node b){
	return a.w<b.w;
}

int tol,n,nn;

void INITW(){
	int i,tmp=1001010011;
	tol=0;
	for(i=0;i<nn;i++){
		if(node[i].w!=tmp){
			tmp=node[i].w;
			node[i].w=++tol;
		}
		else{
			node[i].w=tol;
		}
	}
}

int tree[210000];

int lowbit(int x){
	return x&-x;
}

int fk(int k){  
	int i,now=0;
	for(i=20;i>=0;i--){
		now|=(1<<i);
		if(now>=tol || tree[now]>=k)
			now^=(1<<i);
		else k-=tree[now];
	}
	return now+1;
}


void add(int v,int val){
	while(v<=tol){
		tree[v]+=val;
		v+=v&-v;
	}
}

int query(int v){
	int sum=0;
	while(v>0){
		sum+=tree[v];
		v-=v&-v;
	}
	return sum;
}

int main(){
	int res,t,i,j,k,w;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		nn=n+n;
		for(i=0;i<n;i++){
			scanf("%d%d",&node[i].l,&node[i].w);
			node[i].i=1;
		}
		for(i=n;i<nn;i++){
			scanf("%d%d",&node[i].l,&node[i].w);
			node[i].i=0;
		}
		sort(node,node+nn,cmpW);
		INITW(); // 先排序W,然后对其离散化
		sort(node,node+nn,cmpL);
		res=0;
		for(i=0;i<=tol;i++){
			tree[i]=0;
		}
		for(i=0;i<nn;i++){
			if(node[i].i==0){
				add(node[i].w,1);
				continue;
			}
			else{
				k=query(node[i].w);
				if(k==0){
					continue;
				}
				w=fk(k);
				add(w,-1);
				res++;
			}
		}
		printf("%d\n",res);
	}
	return 0;
}


 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值