( poj 2352,poj 3067, poj2481)树状数组题目总结(二)

POJ2352 Stars

题目: 在平面直角坐标系中, 给定n个点的坐标, 每个点都有一个等级, 这个等级就等于这个点左下方的点的个数

分析:

换个说法来讲, 就是求每个点的左下方共有多少个点。

题目中,点的坐标按照 Y 轴从小到大输入。那么我们可以假设在开始的时候坐标系是空白的,我们按照y值的从小到大的顺序把这些带你逐个放到相应位置,我们会发现,在放的过程中,由于按照y的顺序,使得在计算的过程中,只要考虑x的值即可。

【因为我们要求左下方的值,那么y的放入顺序就使得你当前所放的点的位置的上方没有其他点存在,更没有比当前这个点的x值小比当前y的值大的点存在,此时的所有存在于坐标系的点都是在当前点下面的,因此,我们只要求有多少个点比当前x的值小即可】

有一点要明确的就是,对于一个x = num(num代表某个常数)的直线上,可以有无数个点,整数点也会有很多,所以你所求的就是当前坐标系上,num比当前点的x值小的且为整数的所有直线上点的总和。显然,树状数组可以解决这个问题。

那么,我们用树状数组记录x轴的情况,每一次求和就是求x之前的和。

代码如下:

#include <cstdio>

#define M 32010
int n, x, y, level[15010], c[M];

int lowbit( int t ) {
        return t & ( -t );
}
void addPoint ( int r ) {
        while ( r < M ) {
                c[r]++;
                r += lowbit ( r );
        }
}
int sum ( int r ) {
        int s = 0;
        while ( r > 0 ) {
                s += c[r];
                r -= lowbit ( r );
        }
        return s;
}
int main()
{
        while ( scanf("%d", &n) != EOF ) {
                for ( int i = 0; i < 15010; ++i ) c[i] = level[i] = 0;
                for ( int i = 0; i < n; ++i ) {
                        scanf("%d %d", &x, &y);
                        x++;
                        level[sum(x)]++;
                        addPoint(x);
                }
                for ( int i = 0; i < n; ++i ) {
                        printf("%d\n", level[i]);
                }
        }
}

其中,数组level是用来记录每个等级上点的个数。

POJ 2481 Cow

题目:给定n个区间,对于每个区间,求已知的区间中能够包含它的区间数。(所谓包含,区间a和区间b,如果区间a的长度大于b,并且区间的左右端点都在a之内或者一个端点和a重合)

分析:

这道题实际就是求每个区间被多少个其他区间所包含。

很自然的我们会想到,在把左端点从大到小排序的情况下,如果右端点小于它之前的某个区间的右端点,那么这个区间就被那个区间包含,显然求的是序列的逆序数。

但是有一个特殊的情况,如果两个区间的左右端点中有一个重合,但满足包含条件,这个也是要被算进去的,这个需要进行处理一下。

那么怎么处理这个小问题?

其实这就是一个除重的问题,因为序列是有序的,并且使用结构体存储的,这样的话只要判断当前点的左右端点是否和它的前一个点相同,如果相同,那么它的逆序数就等于前一个点的逆序数,否则正常求逆序数即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define Max 100001
int n, ans[Max], c[Max];
struct node{
        int s, e, id;
}cow[Max];
bool cmp ( node a, node b ) {
        if ( a.s == b.s ) return a.e > b.e;
        return a.s < b.s;
}
int Lowbit( int x ) {
        return x & (-x);
}
int Getsum ( int x ) {
        int s = 0;
        for ( ; x > 0; x -= Lowbit(x) ) s += c[x];
        return s;
}
void Update( int x ) {
        for ( ; x <= Max; x += Lowbit(x) ) ++c[x];
}
int main()
{
        while ( scanf("%d", &n) != EOF && 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;
                }
                memset( c,0,sizeof(c));
                sort( cow + 1, cow + 1 + n, cmp );
                int ts = -1, te = -1;
                for ( int i = 1; i <= n; ++i ) {
                        if ( cow[i].s == ts && cow[i].e == te )
                                ans[cow[i].id] = ans[cow[i-1].id];
                        else {
                                ans[cow[i].id] = Getsum(cow[i].e);
                                ts = cow[i].s, te = cow[i].e;
                        }
                        Update( cow[i].e );
                }
                for ( int i = 1; i <= n - 1; ++i ) {
                        printf("%d ", ans[i]);
                }
                printf("%d \n", ans[n-1]);
        }
}

POJ 3067 Japan

题目:两列城市,分别编号从1 到 n,两列城市之间有公路连接,求公路一共有多少个交点。

分析:首先输入的是整数对代表一条路的两个端点,我们很自然地想到,把其中一个端点排序,从小到大,如果其中一个端点相同,那么按照另一个端点从小到大(因为从一个端点出发的两条路一定不相交,所以按另一个端点从小到大排序,这样在求逆序数的时候就不会把这样的数对计算进去);另外,终点相同的路也不会有交点,那么我们要想上一道题一样在构造树状数组时,进行除重,即和上一个点相同,逆序数就等于上一个点的逆序数。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int t, n, m, k;
long long c[1001];
struct node {
        int e, w;
}a[1000010];
bool cmp ( node x, node y ) {
        if ( x.w == y.w ) return x.e > y.e;
        return x.w > y.w;
}
int lowbit ( int x ) {
        return x & ( -x );
}
void addpoint ( int x ) {
        for (; x <= 1001; x += lowbit( x ) ) c[x]++;
}
long long sum ( int x ) {
        long long s = 0ll;
        for ( ; x > 0; x -= lowbit(x) ) s += c[x];
        return s;
}
int main()
{
        while ( scanf( "%d", &t ) != EOF ) {
                int casei = 0;
                while ( t-- ) {
                        casei++;
                        scanf ( "%d%d%d", &n, &m, &k );
                        for ( int i = 1; i <= k; ++i ) scanf( "%d%d", &a[i].e, &a[i].w );
                        memset ( c, 0, sizeof(c) );
                        long long ans = 0;
                        sort ( a + 1, a + 1 + k, cmp );
                        for ( int i = 1; i <= k; ++i ) {
                                ans += sum ( a[i].e - 1 );
                                addpoint ( a[i].e );
                        }
                        printf ( "Test case %d: %lld\n", casei, ans );
                }
        }
}


你可能感兴趣的:(( poj 2352,poj 3067, poj2481)树状数组题目总结(二))