http://poj.org/problem?id=2352
http://acm.zstu.edu.cn:8080/JudgeOnline/showproblem?problem_id=3106
(1)解题时想用上线段树,题中数据已经排好序了,排序以 y 为主,以 x 为辅。而每颗星星的等级是两个坐标均不大于该星星的总数目(不包括自己),
所以 y 的值是“无用的数据”。只需要知道有多少个其他星星的 x 小于该星星自身的 x ,即为该星星的等级数。可以用线段树加速这个过程。
(2)第一种方法用的不是线段树:
1)神奇的位运算:
int lowbit(int x) { return x&(-x); }
返回该数二进制下的最低位非零位所对应的值(尝试输出 x 从1到9的结果,均为
2的幂次)。
2)优化了的 count 数组。数组中count[1<<i] 表示的是不大于1<<i 的星星个数。
其他零散的 count 记录的是剩余的“遗漏值”(较难理解,细细体会)。
3)计算各个星星的级数时,用二进制一一去处尾部的非零位,以便于收集零散值。
3)未解的疑惑:评级、更新为什么要从 x+1 开始?
若从 x 开始,案例可以通过,但是会超时。
(3)很基本的线段树:
1)之所以没有想通,是因为不会用线段树完成星星的评级。设置变量 grade ,
记录的是星星的现有级数。本质上是最底层的 sum 的加和(这个和的加和
是最大的难点难点)。对 grade 的处理是核心要素,详见代码二。
2)长度默认为 N 。
方法一具体代码:
View Code
#include<stdio.h> #include<string.h> const int N=32000+10; int count[N], level[N]; int n; int lowbit(int x) { return x&(-x); } int grade(int x) { int sum=0; while(x>0) { sum+=count[x]; x-=lowbit(x); } return sum; } void update(int x) { while(x<N) { count[x]++; x+=lowbit(x); } } int main() { int i, j; while(scanf("%d", &n)!=EOF) { memset(count,0 ,sizeof(count)); memset(level, 0, sizeof(level)); for(i=1;i<=n;i++) { int x, y; scanf("%d%d", &x, &y); level[grade(x+1)]++; update(x+1); } for(i=0;i<n;i++) printf("%d\n", level[i]); } return 0; }
方法二具体代码:
View Code
#include<stdio.h> #include<string.h> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 const int N=32200; int sum[N<<2], level[N]; int n; void pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void update(int p, int grade, int l, int r, int rt) { if(l==r) { level[grade+sum[rt]]++; sum[rt]++; return ; } int m=(l+r)>>1; if(p<=m) update(p, grade, lson); else update(p, grade+sum[rt<<1], rson); pushup(rt); } int main() { int i, j; int x, y; while(scanf("%d", &n)!=EOF) { memset(sum, 0, sizeof(sum)); memset(level, 0, sizeof(level)); for(i=1;i<=n;i++) { scanf("%d%d", &x, &y); update(x, 0, 0, N-1, 1); } for(i=0;i<n;i++) printf("%d\n", level[i]); } return 0; }
