【ICPC-407】poj 2352 Stars

点击打开链接poj2352

思路:树状数组
分析:
1 题目是要求出每一个点的左下(正左+正下)有几个星星,那个这个点就是第几层,最后输出0~n-1层的点的个数。比如样列编号为5的星星,左下有3个星星那么5就处于第三层
2 利用树状数组,我们知道树状数组C中,C[i]表示的是原先数组A中的某一段和。题目明确指出输入的时候是按照y值增大的顺序(y相同是x增大),那么我们应该要用什么做为原先的数组A呢,很显然就是X轴,就是A[2]表示x = 2的点的个数。那么当新加入一个点的x值为2的时候,A[2]++,这个时候就是要更新C[2],C[2+lowbit(2)]...

3 那怎么求某个点的左下有几个星星呢,很显然处在左下的点的x值肯定小于等于当前点的,那么就是相当与树状数组求和,那么整道题的思路就完成

4 注意更新树状数组的时候,注意x<=MAXN,不是输入的n。因为更改了A[x],就要更新C[x] , C[x+lowbit(x)]...

5 题目输入的x的坐标可能为0,所以这边我们把所有的x+1,这就避免了TLE

代码:

 

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN = 32010;

int n , ans[MAXN];
int treeNum[MAXN];

int lowbit(int x){
    return x&(-x);
}

int getSum(int x){
    int sum = 0;
    while(x){
        sum += treeNum[x]; 
        x -= lowbit(x);
    }
    return sum;
}

void add(int x , int val){
    while(x < MAXN){
        treeNum[x] += val; 
        x += lowbit(x);
    }
}

int main(){
    int x , y;
    while(scanf("%d" , &n) != EOF){ 
        memset(treeNum , 0 , sizeof(treeNum));
        memset(ans , 0 , sizeof(ans));
        for(int i = 0 ; i < n ; i++){
            scanf("%d%d" , &x , &y); 
            x++;
            ans[getSum(x)]++; 
            add(x , 1);
        }
        for(int i = 0 ; i < n ; i++)
            printf("%d\n" , ans[i]);
    }
    return 0;
}

 

 

 

 

 

思路:线段树+单点更新
分析:
1 题目输入的点是按照y的升序,相同y是按照x升序。那么我们就可以知道后面输入的点肯点不会在前面点的左下(包括正左+正下),那么我们只要考虑即可。这样就变成了一个一维的问题,可以利用线段树来做。
2 我们知道一个点在另外一个点的左下,那么这个点的x值肯定是小于等于另外一个点。那么新加入一个点就是等价于更新区间[x,x],然后要求左下有几个点就是询问区间[0,x]的和。
3 知道了思路,那么就可以利用线段树求出。这一题由于输入数据是有序的,后面的不影响前面的,那么我们可以在输入之后更新之前求出之前点的level。以此类推求出所有。
4 注意x值可能为0,所以根节点区间是[0,MAXN].

代码:

 

 

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

#define MAXN 32010

struct Node{
  int left;
  int right;
  int sum;
};
Node node[4*MAXN];
int vis[MAXN];

/*建立一颗线段树*/
void buildTree(int left , int right , int pos){
   node[pos].left = left;
   node[pos].right = right;
   node[pos].sum = 0;
   if(left == right)
     return;
   int mid = (left+right)>>1;
   buildTree(left , mid , pos<<1);
   buildTree(mid+1 , right , (pos<<1)+1);
}

/*单点更新*/
void update(int index , int pos){
   if(node[pos].left == node[pos].right){
      node[pos].sum++;
      return;
   }
   int mid = (node[pos].left+node[pos].right)>>1;
   if(index <= mid)
     update(index , pos<<1);
   else
     update(index , (pos<<1)+1);
   node[pos].sum = node[pos<<1].sum + node[(pos<<1)+1].sum;
}

/*区间查询*/
int query(int left , int right , int pos){
   if(node[pos].left == left && node[pos].right == right)
     return node[pos].sum;
   int mid = (node[pos].left+node[pos].right)>>1;
   if(right <= mid)
     return query(left , right , pos<<1);
   else if(left > mid)
     return query(left , right , (pos<<1)+1);
   else
     return query(left , mid , pos<<1)+query(mid+1 , right , (pos<<1)+1);
}

int main(){
   int Case , n;
   int x , y;
   scanf("%d" , &Case);
   n = Case;
   buildTree(0 , MAXN , 1);
   memset(vis , 0 , sizeof(vis));
   while(Case--){
      scanf("%d%d" , &x , &y);
      vis[query(0 , x , 1)]++;
      update(x , 1);
   }
   for(int i = 0 ; i < n ; i++)
      printf("%d\n" , vis[i]);
   return 0;
}

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值