首先明确问题,对于每栋楼房的斜率K=H/X,问题就是问有多少个楼房的K比前面所有楼房的K都要大。
这题树套树当然可以,但是挺麻烦的,本渣觉得最简单就是分块……
将N个楼房分成T块,不断维护每个块内楼房的可视序列,如一个块内楼房的高度分别为(3 1 4 2 6 7)那么这个块内楼房的可视序列就是(3 4 6 7)(注意不同的块内是不干扰的,如第一个块可视序列为(3 4 6),第二块的序列可以是(5 7 8))
对于每个修改,我们只需对每个块内的楼房暴力维护可视序列就行了,O(N/T)
对于每个询问,我们只需一个块一个块看,不断维护到目前为止的可视序列中K的最大值kmax(不在可视序列内的楼房的K值一定不大),那么对于查询每个块的时候,可以二分可视序列找到第一个大于kmax的位置,若没有则这个块的所有楼房都不可见,如果存在,那么这个位置后的此块中的可视序列楼房都能看见,那么就更新答案和kmax,不断往后做
注意:毕竟时间比较紧,所以常数还是尽可能写小点,二分和max函数还是自己写好些,不然会爆RP…
1 #include<cstdio> 2 #include<cmath> 3 const int maxn=100005,sqrn=350; 4 int h[maxn],v[sqrn][sqrn],c[sqrn],l[sqrn],r[sqrn],bel[maxn],n,m,block,cnt,x,y,ch; 5 void read(int&x) { 6 x=0,ch=getchar(); 7 while(ch>'9'||ch<'0')ch=getchar(); 8 while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); 9 } 10 bool vis(int x,int y) { 11 return (long long)h[x]*y<(long long)h[y]*x; 12 } 13 int bfind(int*a,int l,int r,int mx) { 14 if(!mx)return l; 15 if(!vis(mx,a[r]))return 0; 16 int m=0,ans=0; 17 while(l<=r) { 18 m=(l+r)>>1; 19 if(vis(mx,a[m]))ans=m,r=m-1; 20 else l=m+1; 21 } 22 return ans; 23 } 24 void build(int x) { 25 c[x]=0; 26 int lst=0; 27 for(register int i=l[x]; i<=r[x]; ++i)if(h[i])if(!lst||vis(lst,i))lst=v[x][++c[x]]=i; 28 } 29 int query() { 30 int tot=0,mx=0,pos; 31 for(register int i=1; i<=cnt; ++i)if(c[i]) { 32 pos=bfind(v[i],1,c[i],mx);; 33 if(pos)tot+=c[i]-pos+1,mx=v[i][c[i]]; 34 } 35 return tot; 36 } 37 int main() { 38 read(n),read(m); 39 block=sqrt(n); 40 cnt=n/block; 41 if(n%block)++cnt; 42 for(register int i=1; i<=n; ++i)bel[i]=(i-1)/block+1; 43 for(register int i=1; i<=cnt; ++i)l[i]=(i-1)*block+1,r[i]=i*block; 44 r[cnt]=n; 45 for(register int i=1; i<=m; ++i) { 46 read(x),read(y); 47 h[x]=y; 48 build(bel[x]); 49 printf("%d\n",query()); 50 } 51 return 0; 52 }