【BZOJ2731】三角形覆盖问题
想象一条平行于\(y\)轴的扫描线,从低往高扫描。如何确定关键高度才能使每两个关键高度之间分割出的图形易于计算呢?
关键高度有:三角形底边高度、三角形上顶点高度、三角形交点的高度。
如此分割,我们会发现,相邻两条扫描线之间的图形,是一个梯形,那么维护每条扫描线上的有效线段长度即可。
如何维护呢?用一个数组\(a_i\)表示第\(i\)格被三角形覆盖了多少次,用一个双向链表记录与每一个与当前扫描线相交的三角形的相交的部分\([l,r]\)。
扫描线遇到三角形底端时,将\(a\)的\([x,x+d-1]\)全部加上1,往双向链表中加入\([x,x+d-1]\)。
上移扫描线一格的方法是:遍历链表,对于每一个元素\([l,r]\),将\(a_r\)减1,将\(r\)减1,如果\(l>r\),删除这个元素。什么意思呢?上移一格时,每一个相交的三角形的相交部分\([l,r]\)的最右端会变为不相交,即\(a_{r}\)减1,同时使\(r\)减1。如此操作,三角形在不相交的时候会被自动删除。
鉴于这道题的神秘数据,直接一格一格向上移动就可以过了...... 如果要优化的话,删除掉那些被其他三角形包含的三角形,就能大幅提升速度。
Code
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=10005,L=2000005;
int n;
int s[L],sum;
ll ans;
struct Triangle{int x,y,d;}a[N];
struct Data{int l,r,pre,nex;}b[N];
int h,cnt;
void insert(int x,int y){
b[++cnt]=(Data){x,y,0,0};
int th=h;
h=cnt; b[cnt].nex=th; b[th].pre=cnt;
}
void delet(int u){
int pt=b[u].pre,nt=b[u].nex;
if(pt)
b[pt].nex=nt;
else h=nt;
if(nt)
b[nt].pre=pt;
}
inline bool cmp(const Triangle &u,const Triangle &v){
if(u.y!=v.y) return u.y<v.y;
if(u.x!=v.x) return u.x<v.x;
return u.d<v.d;
}
int main(){
scanf("%d",&n);
int topy=0;
for(int i=1;i<=n;i++){
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].d);
topy=max(topy,a[i].y+a[i].d);
}
sort(a+1,a+1+n,cmp);
ans=0;
sum=0;
int last=0,lasty=0;
for(int y=0,i=1;y<topy;y++){
for(;a[i].y==y&&i<=n;i++)
if(a[i].d){
for(int k=0;k<a[i].d;k++){
if(!s[a[i].x+k]) sum++;
s[a[i].x+k]++;
}
insert(a[i].x,a[i].x+a[i].d-1);
}
last=sum;
for(int k=h,nk;k;k=nk){
nk=b[k].nex;
s[b[k].r]--;
if(!s[b[k].r]) sum--;
b[k].r--;
if(b[k].r<b[k].l) delet(k);
}
ans+=1LL*(last+sum);
}
printf("%.1lf\n",1.0*ans/2.0);
return 0;
}