背景:
今天要体育考试(
10
10
10天没上体育课)。
狂掉
3
3
3次鞋带,系了
2
2
2次,还差
5
s
5s
5s,第一次体育没满分。体育中考还有
1
w
e
e
k
1week
1week,不要掉鞋带!!!
题目传送门:
https://www.luogu.org/problemnew/show/P4198
题意:
一个二维的坐标系从
(
0
,
0
)
(0,0)
(0,0)点看楼房,问最多能看到多少栋,支持修改操作。
思路:
还是比较妙的。
用线段树来维护可以看到的楼房以及斜率。
考虑左边对右边的影响,若
k
左
边
≥
k
右
边
k_{左边}≥k_{右边}
k左边≥k右边,线段树维护
k
k
k值的大小。答案的贡献就为
左
子
树
的
贡
献
+
右
子
树
的
贡
献
左子树的贡献+右子树的贡献
左子树的贡献+右子树的贡献,左子树的贡献可以直接计算,右子树的贡献通过
k
左
子
树
k_{左子树}
k左子树来比较大小得到即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node{int lc,rc,tot;double k;} tr[300010];
int n,m,len=0;
void build(int l,int r)
{
int now=++len,mid=(l+r)>>1;
tr[now]=(node){-1,-1,0,0.0};
if(l<r)
{
tr[now].lc=len+1; build(l,mid);
tr[now].rc=len+1; build(mid+1,r);
}
}
int calc(int now,int l,int r,double k)
{
if(l==r) return tr[now].k>k;
int lc=tr[now].lc,rc=tr[now].rc,mid=(l+r)>>1;
if(tr[lc].k<=k) return calc(rc,mid+1,r,k); else return tr[now].tot-tr[lc].tot+calc(lc,l,mid,k);
}
void change(int now,int l,int r,int x,double k)
{
if(l==r)
{
tr[now].tot=1;
tr[now].k=k;
return;
}
int lc=tr[now].lc,rc=tr[now].rc,mid=(l+r)>>1;
if(x<=mid) change(lc,l,mid,x,k); else change(rc,mid+1,r,x,k);
tr[now].k=max(tr[lc].k,tr[rc].k);
tr[now].tot=tr[lc].tot+calc(rc,mid+1,r,tr[lc].k);
}
int main()
{
int x,y;
scanf("%d %d",&n,&m);
build(1,n);
for(int i=1;i<=m;i++)
{
scanf("%d %d",&x,&y);
change(1,1,n,x,(double)y/x);
printf("%d\n",tr[1].tot);
}
}