【BZOJ】1035: [ZJOI2008]Risk-最小左转法&点定位(set)

该博客介绍了如何使用最小左转法解决ZJOI2008中关于平面图的问题。在面之间不存在包含关系时,可以将平面图转换为对偶图,利用O(mlogm)的set数据结构来求解。在处理包含关系时,通过特定的连线方式保持连通性,并保持平面图的分割特性。博主提醒在实现代码时有许多需要注意的细节,例如set中边的比较函数和边界处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:bzoj1035


题解

首先考虑面之间不存在包含关系的情况(图连通):

直接最小左转法平面图转对偶图。

除了 m n mn mn暴力求出每个军队所属平面的方法外,存在一种 s e t set set维护类似于扫描线的 O ( m log ⁡ m ) O(m\log m) O(mlogm)做法:对于所有 l e . e d . x > l e . s t . x le.ed.x>le.st.x le.ed.x>le.st.x的边,在 s e t set set中维护所有经过当前 x x x坐标的边从上到下的相对位置关系(平面图中边不相交,所以必然存在绝对的相对位置关系)。扫到每个军队点时, l o w e r b o u n d lower_bound lowerbound找到它在它上面的最近的一条边的反向边所属平面(有向边左边是限制平面),即该点所属平面。

对于面之间存在包含关系的情况,同样可以用 s e t set set维护边信息,记录每个连通分量最上最左的一个点,将其与它上面最近的一条边连边,如下图:
在这里插入图片描述
内部的 a a a点向它上面最近的一条边(红边)的起点连线。

这样整个图就是连通的了,且平面图分割连通性保持不变。

代码十分毒瘤!要考虑的细节比较多。
p.s.
注意每条 l e . e d . x > l e . s t . x le.ed.x>le.st.x le.ed.x>le.st.x的边在扫描线中范围是左闭右开的,扫到某一 x x x值时要先删去所有 e d . x = = x ed.x==x ed.x==x的边。

s e t set set中比较函数写的时候需要注意(假设边 a a a在边 b b b上面)不能只判断 a . s t , a . e d a.st,a.ed a.st,a.ed是否都在 b b b的左侧,存在特例(如下图):
在这里插入图片描述
此时 a . e d a.ed a.ed实际上是在 b b b的右侧的,但在 x = b . e d . x x=b.ed.x x=b.ed.x之前, a a a始终在 b b b上面,所以如果 b . s t , b . e d b.st,b.ed b.st,b.ed都在 a a a右侧也可以。

就是因为这个判断没想到 R E RE RE了5+次。


代码

#include<bits/stdc++.h>
#define mkp make_pair
#define pb push_back
#define ops(x) ((x&1)?(x+1):(x-1))
using namespace std;
typedef double db;
const int N=1e4+100,M=2e5+10;
const db eps=1e-8;

int n,m,cot,num,lim,sld[N];
int bel[N],tg[M],ans[N],tot;
map<int,int>mp[N<<1];
vector<int>nt[N];
bool vs[N],g[N][N];

struct P{
   int x,y;
   P(int x_=0,int y_=0):x(x_),y(y_){};
   inline P operator +(const P&ky){return P(x+ky.x,y+ky.y);}
   inline P operator -(const P&ky){return P(x-ky.x,y-ky.y);}
   inline int operator ^(const P&ky){return x*ky.y-y*ky.x;}
   inline P operator *(const db&ky){return P(x*ky,y*ky);}
   inline P operator /(const db&ky){return P(x/ky,y/ky);}
}p[N];

struct Line{
	P st,ed,dir;db ag;int x,y;
	Line(){};
	Line(P st_,P ed_,int x_,int y_)
	{st=st_;ed=ed_;dir=ed-st;ag=atan2(dir.y,dir.x);x=x_;y=y_;}
}le[M];

int mst,aft;
struct afr{
   int u,v,typ,id;
   afr(){};
   afr(int u_,int v_,int tp_){u=u_;v=v_;typ=tp_;}
   bool operator <(const afr&ky)const{
      if(p[u].x^p[ky.u].x) return p[u].x<p[ky.u].x;
	  return typ<ky.typ;
   } 
}af[M*3];

inline int dcmp(db x){if(fabs(x)<eps) return 0;return x>0?1:-1;}

void et(int x)
{
   vs[x]=true;
   if((p[x].y>p[mst].y)||(p[x].y==p[mst].y && p[x].x<p[mst].x)) mst=x;
   for(int j,i=nt[x].size()-1;i>=0;--i) if(!vs[(j=le[nt[x][i]].y)]) et(j);
}

struct cmp{
	inline bool operator()(const afr&a,const afr&b){
	   if(a.u==a.v) return ((p[b.v]-p[a.u])^(p[b.u]-p[a.u]))<0;
	   if(b.u==b.v) return ((p[a.v]-p[b.u])^(p[a.u]-p[b.u]))>0;
	   int aa=(p[b.v]-p[a.u])^(p[b.u]-p[a.u]),bb=(p[b.v]-p[a.v])^(p[b.u]-p[a.v]),
	   cc=(p[a.v]-p[b.u])^(p[a.u]-p[b.u]),dd=(p[a.v]-p[b.v])^(p[a.u]-p[b.v]);
	   return (aa<0 && bb<=0)||(aa<=0 && bb<0)||(cc>0 && dd>=0)||(cc>=0 && dd>0);
	//不能只判(aa<0 && bb<=0)||(aa<=0 && bb<0)!
	} 
};

set<afr,cmp>T;
set<afr>::iterator si;
inline void merge()
{
	int i,j;
	for(i=1;i<=cot;++i) if(nt[i].size() && (!vs[i]))
	 {et((mst=i));af[++aft]=afr(mst,mst,2);}
	for(i=1;i<=lim;++i) if(le[i].st.x<le[i].ed.x)
	 af[++aft]=afr(le[i].x,le[i].y,1),
	 af[++aft]=afr(le[i].y,le[i].x,0);
	sort(af+1,af+aft+1);
	for(i=1;i<=aft;++i)
		switch(af[i].typ){ 
			case 1: T.insert(af[i]);break;
			case 0: T.erase(T.find(afr(af[i].v,af[i].u,1)));break;
			case 2:
			  if((si=T.lower_bound(af[i]))!=T.begin()){
			  	  j=(--si)->u;				  
				  le[++m]=Line(p[j],p[af[i].u],j,af[i].u);nt[j].pb(m);
				  le[++m]=Line(p[af[i].u],p[j],af[i].u,j);nt[af[i].u].pb(m);
			  }
			  break;
		}
}
inline void ins()
{
	int i;T.clear();aft=0;
	for(i=1;i<=n;++i) af[++aft]=afr(sld[i],sld[i],2);
	for(i=1;i<=m;++i) if(le[i].st.x<le[i].ed.x)
	  af[++aft]=afr(le[i].x,le[i].y,1),af[aft].id=tg[ops(i)],
	  af[++aft]=afr(le[i].y,le[i].x,0);
	sort(af+1,af+aft+1);
	for(i=1;i<=aft;++i)
	  switch(af[i].typ){
	      case 1: T.insert(af[i]);break;
	      case 0: T.erase(T.find(afr(af[i].v,af[i].u,1)));break;
	      case 2:
	      	si=--T.upper_bound(af[i]);
	      	bel[af[i].u]=(si)->id;break;
	  }
	 
}

inline int getid(int x,int y)
{
	x+=10000; 
	if(mp[x].find(y)!=mp[x].end()) return mp[x][y];
	p[++cot]=P(x-10000,y);return (mp[x][y]=cot);
}

inline bool nt_cmp(int x,int y)
{return dcmp(le[x].ag-le[y].ag)?le[x].ag<le[y].ag:(le[x].dir^le[y].dir)>0;}

int ot;
vector<int>::iterator it;
inline void dfs(int nw,int id)
{
	int i,j,st=le[nw].x,pos=le[nw].y;tg[nw]=id;
	int sum=p[st]^p[pos];
	for(;st^pos;){
		it=--upper_bound(nt[pos].begin(),nt[pos].end(),ops(nw),nt_cmp);
		if(it==nt[pos].begin()) it=nt[pos].end();nw=*(--it);
		j=le[nw].y;sum+=p[pos]^p[j];pos=j;tg[nw]=id;
	}
	if(sum<0) ot=id;
}

int main(){
	int i,j,qx,qy,x,y;P a,b;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i) 
	  {scanf("%d%d",&x,&y);sld[i]=getid(x,y);}
	for(i=1;i<=m;++i){
		scanf("%d%d",&a.x,&a.y);qx=getid(a.x,a.y);
		scanf("%d%d",&b.x,&b.y);qy=getid(b.x,b.y);
		j=(i<<1)-1;
		le[j]=Line(a,b,qx,qy);le[j+1]=Line(b,a,qy,qx);
		nt[qx].pb(j);nt[qy].pb(j+1);
	}
	m<<=1;lim=m;merge();
	
	for(i=1;i<=cot;++i) sort(nt[i].begin(),nt[i].end(),nt_cmp);
	for(i=1;i<=m;++i) if(!tg[i]) dfs(i,++num);
	
	ins();
	for(i=1;i<=m;i+=2) if((tg[i]^ot)&&(tg[i+1]^ot))
	  g[tg[i]][tg[i+1]]=g[tg[i+1]][tg[i]]=1;
	for(i=1;i<=n;++i){
	   for(tot=0,j=1;j<=n;++j) if((i^j)&&g[bel[sld[i]]][bel[sld[j]]]) 
	      ans[++tot]=j;
	   printf("%d",tot);
	   for(j=1;j<=tot;++j) printf(" %d",ans[j]);
	   if(i<n) puts("");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值