poj 2481 Cows(树状数组)

题目链接:http://poj.org/problem?id=2481

题目大意:

1 题目给定n头牛所在的区间,然后问每头牛都有几头牛比它强壮
2 根据题目如果牛i的区间是[Si , Ei],牛j的区间是[Sj , Ej]那么牛i要比牛j强壮的话那么就有Si <= Sj && Ei >= Ej && Si-Ei != Sj-Ej;

思路:

一:总体的思路

先对每个牛排序,然后对每头牛查找比他的力量大的牛的个数

二:牛的排序

struct node
{
	int s,e;
	int number;
	bool operator <(const node &tmp)const
	{
		//按照左边界的顺序排序  当左边界相同时  力量大的在前  即右边界大的在前
		if(s!=tmp.s)
			return s<tmp.s;
		else
			return e>tmp.e;
	}
	bool operator ==(const node &tmp)const
	{
		return s==tmp.s&&e==tmp.e;
	}
}nodes[MAXN];
因为我们之后是对每头牛查找力量比他的牛,所以先按照左边界的顺序排序  ,即保证之前牛的左边界一定比他小,即在他之后的牛一定不可能力量比他的,比他力量大的牛只可能在之前,所以当左边界相同时 ,也需要力量大的在前  ,即右边界大的在前.


还有需要注意的是:

ans[nodes[i].number]+=i-getSum(id-1);

因getsun求得是【1,x】而不是【x,maxn)所以,转化为之前的个数减去小于他的个数。

为什么是小于他的个数,即为什么是id-1?

因为他左边界已经不同,即使右边界相同,力量也会比他大!!

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN =100010;

struct node
{
	int s,e;
	int number;
	bool operator <(const node &tmp)const
	{
		//按照左边界的顺序排序  当左边界相同时  力量大的在前  即右边界大的在前
		if(s!=tmp.s)
			return s<tmp.s;
		else
			return e>tmp.e;
	}
	bool operator ==(const node &tmp)const
	{
		return s==tmp.s&&e==tmp.e;
	}
}nodes[MAXN];
int n;
int ans[MAXN];
int treenum[MAXN];

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

int getSum(int x){//一维
    int sum = 0;
    while(x){//将以x为根节点的数 相当于求其叶节点的和  但其非叶节点等于其两个子节点的和
        sum += treenum[x];
        x -= lowbit(x);
    }
    return sum;
}

void add(int x , int val){//一维
    while(x < MAXN){//相当于一个树  他将其自己以及他的父节点一路向上都加val
         treenum[x] += val;
         x += lowbit(x);
    }
}
void solve()
{
	memset(ans,0,sizeof ans);
	memset(treenum,0,sizeof treenum);
	sort(nodes,nodes+n);
	for(int i=0;i<n;i++)
	{
		int id=nodes[i].e;
		if(i&&nodes[i]==nodes[i-1])
			ans[nodes[i].number]=ans[nodes[i-1].number];
		else
		{
			ans[nodes[i].number]+=i-getSum(id-1);//为什么要id-1? 因为他是之前的个数减去小于他的个数,即求大于等于他的个数
													// 他左边界已经不同 右边界可以相同
		}
		add(id,1);
	}
	 printf("%d" , ans[0]);
    for(int i = 1 ; i < n ; i++)
       printf(" %d" , ans[i]);
    printf("\n");
}
int main(){
	while(~scanf("%d",&n)&&n)
	{
		for(int i=0;i<n;i++)
		{
			scanf("%d%d",&nodes[i].s,&nodes[i].e);
			nodes[i].number=i;
		}
		solve();
	}
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值