BZOJ - 2957 楼房重建(分块)

2957: 楼房重建

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 692   Solved: 327
[ Submit][ Status][ Discuss]

Description

  小A的楼房外有一大片施工工地,工地上有N栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。
  为了简化问题,我们考虑这些事件发生在一个二维平面上。小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度。如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。
  施工队的建造总共进行了M天。初始时,所有楼房都还没有开始建造,它们的高度均为0。在第i天,建筑队将会将横坐标为Xi的房屋的高度变为Yi(高度可以比原来大---修建,也可以比原来小---拆除,甚至可以保持不变---建筑队这天什么事也没做)。请你帮小A数数每天在建筑队完工之后,他能看到多少栋楼房?

Input

  第一行两个正整数N,M
  接下来M行,每行两个正整数Xi,Yi

Output


  M行,第i行一个整数表示第i天过后小A能看到的楼房有多少栋

Sample Input


3 4
2 4
3 6
1 1000000000
1 1

Sample Output


1
1
1
2
数据约定
  对于所有的数据1<=Xi<=N,1<=Yi<=10^9
N,M<=100000



思路:每一块维护从这一块开头开始的最长上升序列,查询的时候,对于两头的暴力,中间的二分查找,修改的时候直接暴力修改

刚开始我分成了sqrt(N)块,不知道为什么一直RE,分成0.5*n*logn就AC了

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=100010;
const double eps=1e-6;
const int maxm=210;
int N,M;
int size;
int num[maxn];
double height[maxn];
double block[maxm][maxm*10];
void change(int x,int y)
{
    int x_pos=x/size;
    height[x+1]=(double)y/(x+1);
    num[x_pos]=0;
    double maxv=0;
    for(int i=x_pos*size+1;i<=min(N,(x_pos+1)*size);i++)
        if(height[i]>maxv)block[x_pos][++num[x_pos]]=height[i],maxv=height[i];
}
void query()
{
    int ans=0;
    double maxv=0;
    for(int i=0;i<=(N-1)/size;i++)
    {
        if(num[i]==0)continue;
        ans+=num[i]+1-(upper_bound(block[i]+1,block[i]+num[i]+1,maxv)-block[i]);
        maxv=max(maxv,block[i][num[i]]);
    }
    printf("%d\n",ans);
}
int main()
{
    int x,y;
    scanf("%d%d",&N,&M);
    size=(int)sqrt(N*log(N)/log(2.0)/2);
    memset(height,0,sizeof(height));
    memset(num,0,sizeof(num));
    while(M--)
    {
        scanf("%d%d",&x,&y);
        if(N==1)
        {
            if(y)printf("1\n");
            else printf("0\n");
            continue;
        }
        x--;
        change(x,y);
        query();
    }

    return 0;
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值