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; }
pain and happy in the cruel world.