线段树扫描线(二)矩形周长 以hdu1828为例
还是老规矩,传送门 hdu 1828
依然不做过多解释,给出n个矩形,求这些矩形组合而成的图形的周长(中间镂空的部分也算)
还是像扫面线(一)一样,自下而上扫描,
我们先只考虑横线,发现只要每次加上被覆盖的区间长度的变化值的绝对值就能统计出横向的答案,
但竖线怎么办?
有一种朴素的办法,自下而上扫过一次后,从左到右在扫一遍,常数问题(好像不卡)
接下来,讲一种更好的办法。
我所说的办法,就是在自下而上扫描时,同时统计出竖线的长度和。
每条竖线的长度很好统计,用这一条扫描线的高度减去上一条扫描线的的高度即可,但有多少条竖线呢?
我们可以在线段树中添加三个成员,这样总共就会有五个:
struct node { int dis; //被覆盖的长度 int cnt; //被整体覆盖了几次 int ls; //左端点是否被覆盖 int rs; //右端点是否被覆盖 int num; //区间中总共有多少条竖线 };
接下来,考虑pushup()要怎么写
更新dis的部分在此不再赘述,详见最下面代码或者 扫描线(一)
正确的更新竖线的代码是这样的
tre[nc].ls=tre[2*nc].ls; tre[nc].rs=tre[2*nc+1].rs; tre[nc].num=tre[2*nc].num+tre[2*nc+1].num; if(tre[2*nc].rs&&tre[2*nc+1].ls) { tre[nc].num-=2; }
关键点在if里边,画张图
如图所示,L到R的的两个子区间中,有两条竖线在和并之后消失了,其原因是这两条线段刚好覆盖了分点,因而,要加上if来保证其正确性
然后就是一个小细节,重边要先加后减,否则就会多统计重边,排序时在cmp中处理一下即可。
奉上代码
1 #include<cstdio> 2 #include<map> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 map<int,int> mp; 7 int ls[20100],x1,x2,y1,y2; 8 int n,cnt,cd; 9 struct edge 10 { 11 int l,r,val,h; 12 }eds[10100]; 13 struct node 14 { 15 int cov,dis,ls,rs,num; 16 }tre[80100]; 17 int cmp(edge a,edge b) 18 { 19 if(a.h==b.h) 20 { 21 return a.val>b.val; 22 } 23 return a.h<b.h; 24 } 25 int fabs(int a){return a>0?a:-a;} 26 void init() 27 { 28 cnt=1; 29 cd=1; 30 mp.clear(); 31 memset(ls,0,sizeof(ls)); 32 } 33 int fd(int tar) 34 { 35 int l=1,r=cd; 36 while(l<r) 37 { 38 int mid=(l+r)/2; 39 if(ls[mid]<tar) 40 { 41 l=mid+1; 42 } 43 else 44 { 45 r=mid; 46 } 47 } 48 return l; 49 } 50 void pushup(int L,int R,int nc) 51 { 52 if(tre[nc].cov) 53 { 54 tre[nc].dis=ls[R+1]-ls[L]; 55 tre[nc].num=2; 56 } 57 else 58 { 59 if(L==R) 60 { 61 tre[nc].dis=0; 62 tre[nc].ls=0; 63 tre[nc].rs=0; 64 tre[nc].num=0; 65 } 66 else 67 { 68 tre[nc].dis=tre[2*nc].dis+tre[2*nc+1].dis; 69 tre[nc].num=tre[2*nc].num+tre[2*nc+1].num; 70 if(tre[2*nc].rs&&tre[2*nc+1].ls) 71 { 72 tre[nc].num-=2; 73 } 74 tre[nc].ls=tre[2*nc].ls; 75 tre[nc].rs=tre[2*nc+1].rs; 76 } 77 } 78 } 79 void ins(int l,int r,int dt,int nc,int L,int R) 80 { 81 if(l<=L&&r>=R) 82 { 83 tre[nc].cov+=dt; 84 tre[nc].ls+=dt; 85 tre[nc].rs+=dt; 86 if(tre[nc].cov) 87 { 88 tre[nc].dis=ls[R+1]-ls[L]; 89 tre[nc].num=2; 90 } 91 else 92 { 93 if(L==R) 94 { 95 tre[nc].dis=0; 96 tre[nc].ls=0; 97 tre[nc].rs=0; 98 tre[nc].num=0; 99 } 100 else 101 { 102 tre[nc].num=tre[2*nc].num+tre[2*nc+1].num; 103 if(tre[2*nc].rs&&tre[2*nc+1].ls) 104 { 105 tre[nc].num-=2; 106 } 107 tre[nc].ls=tre[2*nc].ls; 108 tre[nc].rs=tre[2*nc+1].rs; 109 tre[nc].dis=tre[2*nc].dis+tre[2*nc+1].dis; 110 } 111 } 112 return; 113 } 114 int mid=(L+R)/2; 115 if(l<=mid) 116 { 117 ins(l,r,dt,2*nc,L,mid); 118 } 119 if(r>mid) 120 { 121 ins(l,r,dt,2*nc+1,mid+1,R); 122 } 123 pushup(L,R,nc); 124 } 125 int main() 126 { 127 while(scanf("%d",&n)!=EOF) 128 { 129 init(); 130 for(int i=1;i<=n;i++) 131 { 132 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 133 eds[cnt].h=y1; 134 eds[cnt].l=x1; 135 eds[cnt].r=x2; 136 eds[cnt].val=1; 137 cnt++; 138 eds[cnt].h=y2; 139 eds[cnt].l=x1; 140 eds[cnt].r=x2; 141 eds[cnt].val=-1; 142 cnt++; 143 if(!mp[x1]) 144 { 145 mp[x1]=1; 146 ls[cd]=x1; 147 cd++; 148 } 149 if(!mp[x2]) 150 { 151 mp[x2]=1; 152 ls[cd]=x2; 153 cd++; 154 } 155 } 156 sort(ls+1,ls+cd); 157 for(int i=1;i<cnt;i++) 158 { 159 eds[i].l=fd(eds[i].l); 160 eds[i].r=fd(eds[i].r); 161 } 162 sort(eds+1,eds+cnt,cmp); 163 ins(eds[1].l,eds[1].r-1,eds[1].val,1,1,cd-1); 164 int last=eds[1].h,ans=tre[1].dis,lasa=ans; 165 for(int i=2;i<cnt;i++) 166 { 167 int n0=tre[1].num; 168 ins(eds[i].l,eds[i].r-1,eds[i].val,1,1,cd-1); 169 ans+=fabs(tre[1].dis-lasa); 170 ans+=(eds[i].h-last)*n0; 171 lasa=tre[1].dis; 172 last=eds[i].h; 173 } 174 printf("%d\n",ans); 175 } 176 }
大家可能会发现这和(一)中的代码神似,没错,鄙人就是根据(一)中的代码加以修改的!!!!!!!!!!!!!!!