POJ - 2481 Cows(线段树单点更新)

题意:

两个区间:[Si, Ei] 和 [Sj, Ej] (0 <= S < E <= 10^5)
若 Si <= Sj,且 Ej <= Ei 且 Ei - Si > Ej - Sj, 则第i个区间覆盖第j个区间,即牛i比牛j强壮。问对于每一头牛i有多少头牛比它强壮。

解析:

这题先一看,不知如何下手。首先,将每个区间的S和E当做是(S,E)一个点,这样子把所有点在坐标系上画出来,你就会发现一个很神奇的现象,题目要求就会变成:问每一个点的左上角有多少个点?

那么这题转化为了,每个点的左上角有多少个点。但是点不是有序排好的,要预处理一下就可以。
如果正常做,那个y是递增的,所以sum和update那个方向就会相反了,这个其实没什么所谓,一样的,排序的时候先y由大到小排,y相同时x由小到大排,这样小小的处理,就变成stars那题了!

首先将E[i]降序排序如果相等,则按S[i]的升序排序。那么执行完这部操作之后前面的
E[i]肯定 >= 后面的E[i],现在只要考虑S[i]就好了,每次计算S[i]之前有多少个点。
统计完之后将S[i]的位置加1,并更新线段树。

注意:

这题需要离散化,如果两头牛的区间相同,则当前牛比它大的个数等于前面牛比它大的个数。

AC代码

#include <cstring>
#include <cstdio>
#include <algorithm>
#define ls o*2
#define rs o*2+1
using namespace std;
const int N = 1e5 + 10;
struct Cow {
    int s, e, id;
}cow[N];
int sumv[N << 2], ans[N];

bool cmp(Cow a, Cow b) {
    if(a.e != b.e)
        return a.e > b.e;
    return a.s < b.s;
}

int ql, qr;
int query(int o, int L, int R) {
    if(ql <= L && R <= qr) return sumv[o];
    int M = (L + R)/2, ret = 0;
    if(ql <= M) ret += query(ls, L, M);
    if(M < qr) ret += query(rs, M+1, R);
    return ret;
}

int p, v;
void modify(int o, int L, int R) {
    if(L == R) {
        sumv[o] += v;
        return ;
    }
    int M = (L + R)/2;
    if(p <= M) modify(ls, L, M);
    else modify(rs, M+1, R);
    sumv[o] = sumv[ls] + sumv[rs];
}

int main() {
    int n;
    while(~scanf("%d", &n) && n) {
        for(int i = 1; i <= n; i++) {
            scanf("%d%d", &cow[i].s, &cow[i].e);
            cow[i].s++, cow[i].e++;
            cow[i].id = i;
        }

        sort(cow+1, cow+1+n, cmp);
        memset(sumv, 0, sizeof(sumv));
        for(int i = 1; i <= n; i++) {
            if(i > 1 && cow[i].s == cow[i-1].s && cow[i].e == cow[i-1].e) {
                ans[cow[i].id] = ans[cow[i-1].id];
            }else {
                ql = 0, qr = cow[i].s;
                ans[cow[i].id] = query(1, 1, n);
            }
            p = cow[i].s, v = 1;
            modify(1, 1, n);
        }

        printf("%d", ans[1]);
        for(int i = 2; i <= n; i++) {
            printf(" %d", ans[i]);
        }puts("");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值