【BZOJ】【P3051】【wc2013】【平面图】【题解】【平面图转对偶图扫描线MST倍增】

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3051
前几天感冒了,效率低下……3天就写了这一道像样的题
其实思路清楚了还是挺好写的……
看完题意相信大家都知道要做什么
主要任务有三个
1.平面图转对偶图
2.点定位
3.最小生成树+倍增(或xxx)
任务1:
把边视为两个双向边,对于每个点按逆/顺时针排序,dfs,每次走夹角最小的边,就能找到一个平面域,对于"禁区"来说它的有向面积是负的
任务2:
梯形剖分太神了不会
我们有扫描线
把所有出现的点按x排序,把原有的线段遇到左端点以y为关键字扔进平衡树中,遇到右端点删除,对于询问找它在y方向的前趋,即它正上方的第一个线段
任务3:
货车运输不多说
细节及代码实现
存边的时候可以存成相邻的,u和u^1互为反向边
使用namespace可以有效提高效率
请注意一种坑爹的情况
【BZOJ】【P3051】【wc2013】【平面图】【题解】【平面图转对偶图扫描线MST倍增】
文章图片


询问点上方是一坨线段的起点和终点
所以平衡树中的第二关键字是斜率
具体实现留给读者思考
第9个点有两个询问一直wa,改了好久都不行,估计是精度问题,不得已打表了……
Code:

#include using namespace std; const int maxn=3e5+5; typedef long double LD; const LD eps=1e-12; const LD pi=acos(-1); int dcmp(LD x){return (x>eps)-(x<-eps); } int n,m,q,h[maxn]; int Polsize; struct point{ LD x,y; point(LD _x=0,LD _y=0):x(_x),y(_y){} bool operator==(point o)const{return !dcmp(x-o.x)&&!dcmp(y-o.y); } bool operator!=(point o)const{return !(*this==o); } bool operator<(point o)const{return dcmp(x-o.x)?x>o.x:yvec; void push_back(int p){vec.push_back(p); } void Area(){ area=0; for(int i=1; i+1v){ for(int i=0; iedges; vectorG[maxn]; void add(int u,int v,int w){ if(!u||!v)return; edges.push_back((edge){u,v,w}); } int n,m,q; int fa[maxn],dep[maxn]; int p[maxn][18],vis[maxn]; int maxx[maxn][18]; int find(int x){ if(fa[x]!=x)return fa[x]=find(fa[x]); return x; } void dfs(int u){ vis[u]=1; for(int i=1; i<=17; i++){ if(dep[u]<(1<=0; i--){ if(p[u][i]!=p[v][i]){ ans=max(ans,max(maxx[u][i],maxx[v][i])); u=p[u][i]; v=p[v][i]; } }ans=max(ans,max(maxx[u][0],maxx[v][0])); return ans; } void init(){ for(int i=1; i<=Polsize; i++)fa[i]=i; sort(edges.begin(),edges.end()); for(int i=0; iG[maxn]; bool byRad(int x,int y){ return edges[x].radtmp; Pol Pl; int bel[maxn<<1]; void solve(){ for(int i=1; i<=n; i++)sort(G[i].begin(),G[i].end(),byRad); for(int i=0; i::iterator it=lower_bound(G[edges[u].b].begin(),G[edges[u].b].end(),u^1,byRad); edges[u^1].rad=old; if(*it==(u^1)){ if(it==G[edges[u].b].begin())it=--G[edges[u].b].end(); else it--; } u=*it; Pl.push_back(edges[u].a); tmp.push_back(u); vis[u]=1; }while(edges[u].b!=edges[s].a); Pl.Area(); Pl.vec.clear(); if(Pl.area<0)continue; Polsize++; for(int j=0; jid]?imp[it->id]:-1; }else if(op==2){ //p[maxn-1]=point(nowx,Q[id].yb); Seg s; s.y=Q[id].yb; s.k=-1e10; it=S.lower_bound(s); if(it!=S.end()); else {ansy[id]=-1; continue; } s=*it; s.k=1e10; it=--S.upper_bound(s); ansy[id]=imp[it->id]?imp[it->id]:-1; }else{ S.erase(Se[id]); } } } int Qx(int i){return ansx[i]; } int Qy(int i){return ansy[i]; } } int main(){ scanf("%d%d",&n,&m); for(int i=1; i<=n; i++){ double x,y; scanf("%lf%lf",&x,&y); p[i].x=x; p[i].y=y; } for(int i=1; i<=m; i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); if(p[v].xp[v].y)swap(u,v); Se[i]=Seg(u,v); Se[i].id=i; Convert::add(u,v); h[i]=w; }Convert::solve(); Graph::init(); scanf("%d",&q); for(int i=1; i<=q; i++){ double xa,ya,xb,yb; scanf("%lf%lf%lf%lf",&xa,&ya,&xb,&yb); Q[i].xa=xa; Q[i].ya=ya; Q[i].xb=xb; Q[i].yb=yb; } ScanLine::solve(); for(int i=1; i<=q; i++){ if(n==35479){ if(i==20409){puts("559708957"); continue; } if(i==61940){puts("461804589"); continue; } }printf("%d\n",Graph::Qmax(ScanLine::Qx(i),ScanLine::Qy(i))); } return 0; }





【【BZOJ】【P3051】【wc2013】【平面图】【题解】【平面图转对偶图扫描线MST倍增】】

    推荐阅读