【JOISC 2020】【LOJ3272】【UOJ502】汉堡肉(2-SAT)

题解:

如果你不去看数据范围很容易就觉得这是一道不可做题。

感觉如果 K K K 再大一点也可能就是 NP-Hard 了。

首先求出左边界最大值 m x L mxL mxL,右边界最小值 m n R mnR mnR,上边界最小值 m n U mnU mnU 和下边界最大值 m x D mxD mxD,显然任何一组解都可以通过平移走到这几条直线上。

K ≤ 3 K\leq 3 K3 的时候由抽屉原理可知必然有至少一个点在这几条线的交点处,暴力dfs即可。

K = 4 K=4 K=4 的时候,我们还是选择先暴力 dfs ,如果没有找到答案,说明 m x L < m n R , m x D < m n U mxL<mnR,mxD<mnU mxL<mnR,mxD<mnU

显然答案的四根竹签应该分别在这四条线上。

如果某个矩形与这四条线中的至少三条相交,说明它完全包含了某条线段,否则它应该更新这四个值中的一个,则我们可以不考虑这个矩形。

剩下的矩形都与这四条线中的 1 − 2 1-2 12 条相交,如果只与一条相交,则该直线的上的竹签必然存在于某个确定的区间中。

否则如果与两条直线相交,我们可以将这个转化为 2-SAT 问题,设这两条线上的点未确定的坐标为 x 1 , x 2 x_1,x_2 x1,x2,则要求某一个矩形包含至少一个点可以转化为 x 1 ∈ [ l 1 , r 1 ] ∨ x 2 ∈ [ l 2 , r 2 ] x_1\in[l_1,r_1]\lor x_2\in[l_2,r_2] x1[l1,r1]x2[l2,r2],对于每个直线上的限制,离散化后前缀连边的套路就不讨论了,考虑怎么处理对于某个矩形的限制。

对于这个矩形,建立两个变量 y 1 , . y 2 y_1,.y_2 y1,.y2 四个点,分别表示它在两条直线上的限制是否满足,有限制 y 1 ∣ ∣ y 2 y_1||y_2 y1y2 为真,连边 ¬ y 1 → y 2 \neg y_1\rightarrow y_2 ¬y1y2 ¬ y 2 → y 1 \neg y_2\rightarrow y_1 ¬y2y1,然后前后缀该怎么满足怎么连边就行了。

下面的代码连边经过了优化,并没有把 y 1 , y 2 y_1,y_2 y1,y2 四个点建立出来,如果需要判断是否有解的话可能就必须要建立出来了。

跑 2-SAT 即可


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}template<typename T>T get_integer(){
		char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
		while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
	}inline int gi(){return get_integer<int>();}
}using namespace IO;

using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define fi first
#define se second

template<typename T>void ckmn(T &a,cs T &b){a>b?a=b:a;}
template<typename T>void ckmx(T &a,cs T &b){a<b?a=b:a;}


cs int N=2e6+7;
cs int INF=1e9;

int n,k;
struct rec{int x1,x2,y1,y2;};
std::vector<rec> vec;

std::vector<rec> rem(cs std::vector<rec> &vec,int x,int y){
	std::vector<rec> res;
	for(cs auto &t:vec)
		if(t.x1>x||t.x2<x||t.y1>y||t.y2<y)
			res.push_back(t);
	return res;
}

bool dfs(cs std::vector<rec> &vec,int k){
	if(vec.empty()){
		while(k--)cout<<"0 0\n";
		return true;
	}if(!k)return false;
	int L=0,R=INF,D=0,U=INF;
	for(cs auto &t:vec){
		ckmx(L,t.x1),ckmn(R,t.x2);
		ckmx(D,t.y1),ckmn(U,t.y2);
	}
	if(dfs(rem(vec,L,D),k-1))
		return cout<<L<<" "<<D<<"\n",1;
	if(dfs(rem(vec,L,U),k-1))
		return cout<<L<<" "<<U<<"\n",1;
	if(dfs(rem(vec,R,D),k-1))
		return cout<<R<<" "<<D<<"\n",1;
	if(dfs(rem(vec,R,U),k-1))
		return cout<<R<<" "<<U<<"\n",1;
	return false;
}

std::vector<int> G[N];
int id[N][2],ct;
bool ok[N];

int dfn[N],low[N];
int scc[N],sct,dfc;
int st[N],tp,ins[N];

void tarjan(int u){
	dfn[u]=low[u]=++dfc;
	ins[u]=true;st[++tp]=u;
	for(int v:G[u]){
		if(!dfn[v])tarjan(v),low[u]=std::min(low[u],low[v]);
		else if(ins[v])low[u]=std::min(low[u],dfn[v]);
	}if(dfn[u]!=low[u])return ;++sct;
	do scc[st[tp]]=sct,ins[st[tp]]=false;while(st[tp--]!=u);
}

void Main(){
	n=gi(),k=gi();vec.resize(n);
	for(auto &t:vec)
		t.x1=gi(),t.y1=gi(),
		t.x2=gi(),t.y2=gi();
	if(dfs(vec,k))return ;
	int x[2]={0,INF},y[2]={0,INF};ct=n<<1;
	for(auto &t:vec){
		ckmx(x[0],t.x1),ckmn(x[1],t.x2);
		ckmx(y[0],t.y1),ckmn(y[1],t.y2);
	}for(int re i=0;i<n;++i){
		bool f[4]={
			vec[i].x2>=x[0],vec[i].x1<=x[1],
			vec[i].y2>=y[0],vec[i].y1<=y[1]
		};if(f[0]+f[1]+f[2]+f[3]>2){
			ok[i]=true;id[i][0]=id[i][1]=-1;
		}else {
			int ct=0;for(int re j=0;j<4;++j)
				if(f[j])id[i][ct++]=j;
			if(ct==1)
				id[i][1]=-1,G[i<<1|1].push_back(i<<1);
		}
	}for(int re i=0;i<4;++i){
		std::vector<int> pt;
		std::priority_queue<pii> q;
		for(int j=0;j<n;++j)
			if(id[j][0]==i||id[j][1]==i)
				pt.push_back(j);
		std::sort(pt.begin(),pt.end(),[i](int a,int b){
			return i<2?vec[a].y1<vec[b].y1:vec[a].x1<vec[b].x1;
		});int p1=-1,p2=-1;
		for(int u:pt){
			q.push(pii(i<2?-vec[u].y2:-vec[u].x2,u));
			while(-q.top().fi<(i<2?vec[u].y1:vec[u].x1)){
				int p=q.top().se;q.pop();bool t=id[p][1]==i;
				if(p1==-1)p1=p<<1|t,p2=p1^1;
				else {
					G[p<<1|t].push_back(ct);
					G[p1].push_back(ct);p1=ct++;
					G[ct].push_back(p<<1|!t);
					G[ct].push_back(p2);p2=ct++;
				}
			}
			if(p1!=-1){
				bool t=id[u][1]==i;
				G[u<<1|t].push_back(p2);
				G[p1].push_back(u<<1|!t);
			}
		}
	}
	for(int re i=0;i<ct;++i)
		if(!dfn[i])tarjan(i);
	int ans[4][2]={};
	for(int re i=0;i<n;++i)if(!ok[i]){
		int p=id[i][scc[i<<1]>scc[i<<1|1]];
		ans[p][0]=std::max(ans[p][0],vec[i].x1);
		ans[p][1]=std::max(ans[p][1],vec[i].y1);
	}for(int re i=0;i<2;++i)
		cout<<x[i]<<" "<<ans[i][1]<<"\n";
	for(int re i=2;i<4;++i)
		cout<<ans[i][0]<<" "<<y[i-2]<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("hamburg.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值