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]); } } }
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]); } }
题目:两列城市,分别编号从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 ); } } }