zoukankan      html  css  js  c++  java
  • 树状数组二维偏序之POJ2481

    很神奇 ,不知道是不是我理解错了,这个题网上很多题解都是错的吧,应该是数据太弱。

    题目链接

    题意:

    就是给出N个区间,问每个区间是多少其它区间的真子集。题目输入很简单输入n个区间的左右端点((S)(E) ,(0 <= (S) < (E) <= (10^5)) ),但是没有说(S<=n)吧。。。

    思路:

    典型的二维偏序关系,第一维先排好序,第二维用树状数组计数即可。
    树状数组手写还是很简单的,记住lowbit操作,然后sum函数和add函数分别向下lowbit和向上lowbit。
    但是要根据题目选择如何排序。比如这个题,求一个区间被多少区间包含,那么两种排序方法:

    1. 右区间从大到小排序,如右区间相同,左区间从小到大排序。
    2. 左区间从小到大排序,如左区间相同,右区间从大到小排序。

    两种排序本质相同,都是先枚举外层区间,然后对另外一个端点计数,建议初学者用笔比划一下。但是第一种排序方法代码简单一点,为什么呢?因为计数时候是计算前一段区间和,这和树状数组sum函数完美契合。而后一种排序方法时计算后一段区间和,需要两端sum函数值相减。
    那为什么说网上的很多题解代码有问题呢?
    因为他们的add函数如下:

    void add(int i,int val)
    {
        while(i<=n)//这里n为区间个数,如果计数区间端点大于n,那么就无法计数了
        {
            c[i]+=val;
            i+=lowbit(i);
        }
    }
    

    例如如下数据,输出就为0 0 0。显然答案应该是1 0 0。

    3
    100 200
    0 300
    300 400
    

    代码:

    多种数据memset使用memset总是感觉很危险。先区间最大值可以不用memset,这里没有求区间最大值,直接使用N-1代替的。另外代码使用第二种排序方式。

    #include <iostream>
    #include<stdio.h>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=1e5+10;
    struct Cow{
        int l,r;
        int id;
    }cow[N];
    //左端点从小到大排序,右端点从大到小排序,这样也可以保证先枚举外层区间
    bool cmp(Cow c1,Cow c2){
        if(c1.l==c2.l)return c1.r>c2.r;
        return c1.l<c2.l;
    }
    int n;
    int bit[N];
    int cnt[N];
    int Lowbit(int i){
        return i&(-i);
    }
    //求和函数,向下lowbit
    int sum(int i){
        int sum=0;
        while(i>0){
            sum+=bit[i];
            i-=Lowbit(i);
        }
        return sum;
    }
    //加操作,向上lowbit
    void add(int i,int x){
        while(i<N){
            bit[i]+=x;
            i+=Lowbit(i);
        }
    }
    int main()
    {
        while(~scanf("%d",&n),n){
            memset(bit,0,sizeof(bit));
            memset(cnt,0,sizeof(cnt));
            for(int i=1;i<=n;i++){
                scanf("%d%d",&cow[i].l,&cow[i].r);
                cow[i].l++;cow[i].r++;
                cow[i].id=i;
            }
            sort(cow+1,cow+n+1,cmp);
            for(int i=1;i<=n;i++){
                if(i>1&&cow[i].l==cow[i-1].l&&cow[i].r==cow[i-1].r)
                    cnt[cow[i].id]=cnt[cow[i-1].id];
                else
                    cnt[cow[i].id]=sum(N-1)-sum(cow[i].r-1);
                add(cow[i].r,1);
            }
            printf("%d",cnt[1]);
            for(int i=2;i<=n;i++)
              printf(" %d",cnt[i]);
            printf("
    ");
        }
        return 0;
    }
    
    

    如有不足,欢迎指出

  • 相关阅读:
    pat 1034 Head of a Gang (30分)
    pta坑点
    Devc++ 编译 c++11
    Invitation Cards dijkstra法
    Cube Stacking
    畅通工程
    蚂蚁上树
    洛谷 P1439 【模板】最长公共子序列
    Recursive sequence
    A Simple Math Problem
  • 原文地址:https://www.cnblogs.com/gzr2018/p/12801969.html
Copyright © 2011-2022 走看看