BZOJ 2957 楼房重建 [分块][线段树]

这道题的线段树做法很是奇巧,正常考试中那种维护的方式还是比较难想到,所以说我先是用分块做的。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=110000;
int n,m,siz,ans,pre,l,q,r,mid;
int t[1000],s[1000],h[maxn],p[1000][1000];
bool cmp(int a,int b){
    return (a==0)?h[b]>0:(long long)h[a]*b<(long long)h[b]*a;
}
int main(){
    scanf("%d%d",&n,&m),n++;
    siz=(int)(0.5*sqrt(n*log(1.0*n)/log(2.0)));
    for(register int a,b,i=1;i<=m;i++){
        scanf("%d%d",&a,&b);
        h[a]=b,p[a/siz][0]=0;
        for(register int j=a/siz*siz;j<(a/siz+1)*siz;j++)
            if(cmp(p[a/siz][p[a/siz][0]],j))p[a/siz][++p[a/siz][0]]=j;
        for(ans=pre=q=0;q*siz<n;q++){
            l=1,r=p[q][0]+1;
            while(l<r){
                mid=(l+r)>>1;
                if(cmp(pre,p[q][mid]))r=mid;
                else l=mid+1;
            }
            ans+=p[q][0]+1-r;
            if(p[q][0]>=l)pre=p[q][p[q][0]];
        }
        printf("%d\n",ans);
    }
    return 0;
}

代码极其少,并且特别好理解。只是分块时所分的块的大小跟平时的 n 不同。而是 12nlogn2 ,这种分块方法可以学习一下。

至于线段树则十分巧妙了
code:
只可意会,不可言传
(其实是懒。。)

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define inf 1000000000
#define ll long long 
using namespace std;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
struct data{
    int l,r,ans;
    double val,lx,mx;
}t[400005];
void build(int k,int l,int r){
    t[k].l=l;t[k].r=r;int mid=(l+r)>>1;
    if(l==r)return;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
}
int cal(int k,double val){
    int l=t[k].l,r=t[k].r;
    if(l==r)return t[k].val>val;
    if(t[k<<1].val<=val)return cal(k<<1|1,val);
    return t[k].ans-t[k<<1].ans+cal(k<<1,val);
}
void modify(int k,int pos,double val){
    int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
    if(l==r){
        t[k].ans=1;
        t[k].val=val;
        return;
    }
    if(pos<=mid)modify(k<<1,pos,val);
    else modify(k<<1|1,pos,val);
    t[k].val=max(t[k<<1].val,t[k<<1|1].val);
    t[k].ans=t[k<<1].ans+cal(k<<1|1,t[k<<1].val);
}
int main(){
    n=read();m=read();
    build(1,1,n);
    while(m--){
        int x=read(),y=read();
        modify(1,x,(double)y/x);
        printf("%d\n",t[1].ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值