bzoj2957: 楼房重建

慌张。。今天代码的正确率也太高了吧,连调都没怎么调就A了呀(莫非是因为不会用win10调试??)

这题就是被亮老师D飞来做的。

一看,这不是维护凸包吗,做cash的时候是大力splay或者cdq

这个好像带修cdq应该不兹瓷

splay。。其实我下了两次决心要写出来。。。但是恐怖的代码量。。

那么学一发线段树

用一个mx表示区间最大斜率,c表示从左端点开始最多看到多少个房子

难点就在于维护

首先肯定直接等于tr[lc].c,考虑右区间有最多有多少个单调上升且第一个点斜率比tr[rc].mx大的斜率

tr[rc].mx<tr[lc].mx显然不行

否则来在rc区间搜一下,假如左子树mx小于k那么去右子树找,否则当前tr[now].c-tr[lc].c肯定全部可以看到(注意这里不是tr[rc].c啊),先加上再去左区间找。

 

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

struct node
{
    int l,r,lc,rc;
    double mx;int c;
}tr[210000];int trlen;
void bt(int l,int r)
{
    int now=++trlen;
    tr[now].l=l;tr[now].r=r;
    tr[now].lc=tr[now].rc=-1;
    tr[now].mx=0.0;tr[now].c=0;
    if(l<r)
    {
        int mid=(l+r)/2;
        tr[now].lc=trlen+1;bt(l,mid);
        tr[now].rc=trlen+1;bt(mid+1,r);
    }
}
int ans;
void findsum(int now,double k)
{
    if(tr[now].l==tr[now].r){ans++;return ;}
    int mid=(tr[now].l+tr[now].r)/2;
    int lc=tr[now].lc,rc=tr[now].rc;
    if(tr[lc].mx<=k) findsum(rc,k);
    else ans+=tr[now].c-tr[lc].c, findsum(lc,k);
}
void change(int now,int x,int y)
{
    if(tr[now].l==tr[now].r)
    {
        tr[now].mx=double(y)/double(x);
        tr[now].c=1;return ;
    }
    int mid=(tr[now].l+tr[now].r)/2;
    int lc=tr[now].lc,rc=tr[now].rc;
    if(x<=mid)change(lc,x,y);
    else       change(rc,x,y);
    
    tr[now].mx=max(tr[lc].mx,tr[rc].mx);
    tr[now].c=tr[lc].c;
    if(tr[lc].mx<tr[rc].mx)
    {
        ans=0;findsum(rc,tr[lc].mx);
        tr[now].c+=ans;
    }
} 
int main()
{
    int n,m,x,y;
    scanf("%d%d",&n,&m);
    trlen=0;bt(1,n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        change(1,x,y);
        printf("%d\n",tr[1].c);
    }
    return 0;
}

 

posted @ 2018-04-22 20:42  AKCqhzdy  阅读(83)  评论(0编辑  收藏