洛谷 题解 P4198 【楼房重建】

首先明确问题,对于每栋楼房的斜率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 }
垃圾鸡脖的代码

猜你喜欢

转载自www.cnblogs.com/Xchu/p/11265867.html
今日推荐