POJ 2481 Cows(树状数组)

POJ 2481 Cows(树状数组)

http://poj.org/problem?id=2481

题意:

        有n头牛,每头牛对应一个区间[Si,Ei],如果牛j 的区间是牛i 的区间的真子集(Si <= Sj and Ej <= Ei and Ei - Si > Ej - Sj),那么就说牛i 比牛j 强壮。要你依次输出比第i头牛强壮的牛数目。

分析:

        对于任意两头牛i和j,只有当Si <= Sjand Ej <= Ei and Ei - Si > Ej – Sj时,i才比j强壮,即:

        如果将所有牛的E区间按从大到小排序(如果E相同,则S小的排在前面)的话,那当前读取到第i个牛的Si和Ei,那么之前(假设任意牛的区间不会完全相同)的牛的Sj(j<=i-1)<=Si的这些牛就都比i号牛强壮了。

        所以我们首先对读取到的区间排序,然后可以用树状数组利用牛的S坐标来算出结果

       本题难点:如果两个区间完全一样,那么这两头牛不能互为强壮。但是这两头牛对后面的更弱的牛有用。

        我的解决方法是:如果第i头牛与第i-1头牛完全相同,那么就用i-1的结果赋值给i的结果。如果i牛与i-1牛不完全相同,就输出前面有多少牛的S值<=S[i]。需要注意的是,牛的S和E坐标可能为0,所以我们对读取的S和E坐标都+1,整体后移一格。

AC代码:891ms

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100000+1000;
int c[MAXN];
int lowbit(int x)
{
    return x&(-x);
}
int sum(int x)//x>=1
{
    int res=0;
    while(x>0)
    {
        res +=c[x];
        x-=lowbit(x);
    }
    return res;
}
void add(int x,int v)//x>=1
{
    while(x<=MAXN)
    {
        c[x] +=v;
        x+=lowbit(x);
    }
}
struct cow
{
    int s,e;
    int index;
    bool operator <(const cow&b)const//e值越大越靠前,e值相同时s值越小越靠前
    {
        return e>b.e||(e==b.e&&s<b.s);
    }
}cows[MAXN];
int ans[MAXN];//ans[i]=x 表有x头牛比i号牛强壮
int main()
{
    int n;
    while(scanf("%d",&n)==1&&n)
    {
        for(int i=0;i<n;i++)
        {
            int s,e;
            scanf("%d%d",&s,&e);
            s++;//以防s值为0
            cows[i].s=s;
            cows[i].e=e;
            cows[i].index=i;//保存初始序号
        }
        sort(cows,cows+n);
        memset(ans,0,sizeof(ans));
        memset(c,0,sizeof(c));

        ans[cows[0].index]=0;//单独处理重新排序后的第0个牛
        add(cows[0].s,1);
        for(int i=1;i<n;i++)
        {
            if(cows[i].s==cows[i-1].s && cows[i].e==cows[i-1].e)//两头牛的区间完全一样
                ans[cows[i].index] = ans[cows[i-1].index];
            else                                                //新的牛区间,与之前的牛区间不同
                ans[cows[i].index] = sum(cows[i].s);
            add(cows[i].s,1);
        }
        for(int i=0;i<n;i++)
            printf("%d ",ans[i]);
        printf("\n");
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值