LOJ #3272. 「JOISC 2020 Day1」汉堡肉 (2-sat)

题目
求出最右的左边界,最左的右边界,最上的下边界,最下的上边界。
K ≤ 3 K\leq 3 K3时必有一个牙签插在两个边界的交点。
那么 O ( 4 ) O(4) O(4)枚举插在哪个交点爆搜,深度是 K K K,时间复杂度 O ( n 4 K ) O(n4^K) O(n4K)

K = 4 K = 4 K=4的时候我们必有一种方案可以在每个最边界上都放一个点。
所以和3个边界以上有交的就一定会被插到,
我们只需要考虑和两个一下边界有交的矩形,
考虑 2 − s a t 2-sat 2sat
那么和两个边界有交的就变成了两种选择(选择在哪个边上被插),在一条边界上,两个矩形无交,那么这两个矩形就不能同时在这条边上被插,前后缀优化建边即可。
和一个边界有交的,就是有一种选择一定不会被选,那么从那种选择往另一种选择连边即可。

A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define maxn 400005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

int n,K;
int L[maxn][2],R[maxn][2],sx[maxn<<1],sy[maxn<<1];
int ans[4][2];

namespace Solver1{
	
	int usd[maxn] , flg = 0;
	
	void dfs(int u){
		int mx[2] = {0,0} , mn[2] = {0x3f3f3f3f,0x3f3f3f3f};
		rep(i,1,n) if(!usd[i]) rep(j,0,1)
			mn[j] = min(mn[j] , R[i][j]) , 
			mx[j] = max(mx[j] , L[i][j]);
		if(!mx[0]) return (void)(flg = 1);
		if(u == K) return;
		ans[u][0] = mn[0] , ans[u][1] = mn[1];
		rep(i,1,n) if(L[i][0] <= ans[u][0] && L[i][1] <= ans[u][1] && ans[u][0] <= R[i][0] && ans[u][1] <= R[i][1])
			usd[i]++;
		dfs(u+1);
		if(flg) return;
		rep(i,1,n) if(L[i][0] <= ans[u][0] && L[i][1] <= ans[u][1] && ans[u][0] <= R[i][0] && ans[u][1] <= R[i][1])
			usd[i]--;
			
		ans[u][0] = mx[0] , ans[u][1] = mx[1];
		rep(i,1,n) if(L[i][0] <= ans[u][0] && L[i][1] <= ans[u][1] && ans[u][0] <= R[i][0] && ans[u][1] <= R[i][1])
			usd[i]++;
		dfs(u+1);
		if(flg) return;
		rep(i,1,n) if(L[i][0] <= ans[u][0] && L[i][1] <= ans[u][1] && ans[u][0] <= R[i][0] && ans[u][1] <= R[i][1])
			usd[i]--;
			
		ans[u][0] = mx[0] , ans[u][1] = mn[1];
		rep(i,1,n) if(L[i][0] <= ans[u][0] && L[i][1] <= ans[u][1] && ans[u][0] <= R[i][0] && ans[u][1] <= R[i][1])
			usd[i]++;
		dfs(u+1);
		if(flg) return;
		rep(i,1,n) if(L[i][0] <= ans[u][0] && L[i][1] <= ans[u][1] && ans[u][0] <= R[i][0] && ans[u][1] <= R[i][1])
			usd[i]--;
			
		ans[u][0] = mn[0] , ans[u][1] = mx[1];
		rep(i,1,n) if(L[i][0] <= ans[u][0] && L[i][1] <= ans[u][1] && ans[u][0] <= R[i][0] && ans[u][1] <= R[i][1])
			usd[i]++;
		dfs(u+1);
		if(flg) return;
		rep(i,1,n) if(L[i][0] <= ans[u][0] && L[i][1] <= ans[u][1] && ans[u][0] <= R[i][0] && ans[u][1] <= R[i][1])
			usd[i]--;
	}
	
	void Solve(){
		dfs(0);
		if(flg){
			for(int i=0;i<K;i++)
				printf("%d %d\n",sx[ans[i][0]] , sy[ans[i][1]]);
			exit(0);
		}
	}
}

namespace Solver2{
	
	int p[maxn][2] , tot = 1;
	#define pii pair<int,int>
	#define mp make_pair
	vector<int>in[4][maxn],ot[4][maxn],cs[maxn];
	
	#define maxp maxn * 10
	#define maxm maxp * 2
	int info[maxp],Prev[maxm],to[maxm],cnt_e;
	void Node(int u,int v){
		if(u && v)
			Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v;
	}
	
	int dfn[maxp],low[maxp],c[maxp],st[maxp],scc,tim;
	void dfs(int u){
		st[++st[0]] = u;
		dfn[u] = low[u] = ++tim;
		for(int i=info[u],v;i;i=Prev[i]) if(!dfn[v=to[i]]) dfs(v),low[u]=min(low[u],low[v]);
			else if(!c[v]) low[u] = min(low[u],dfn[v]);
		if(dfn[u] == low[u]){
			scc++;
			for(int t=-1;t!=u;){
				t=st[st[0]--];
				c[t] = scc;
			}
		}
	}
	
	void Solve(){
		int mx[2] = {0,0} , mn[2] = {0x3f3f3f3f,0x3f3f3f3f};
		rep(i,1,n) rep(j,0,1)
			mn[j] = min(mn[j] , R[i][j]) , 
			mx[j] = max(mx[j] , L[i][j]);
		int lx[4] = {mn[1] , mn[1] , mn[0] , mn[0]} , rx[4] = {mx[1] , mx[1] , mx[0] , mx[0]};
		rep(i,1,n){
			vector<int>&s=cs[i];
			if(L[i][0] <= mn[0] && mn[0] <= R[i][0]) s.push_back(0);
			if(L[i][0] <= mx[0] && mx[0] <= R[i][0]) s.push_back(1);
			if(L[i][1] <= mn[1] && mn[1] <= R[i][1]) s.push_back(2);
			if(L[i][1] <= mx[1] && mx[1] <= R[i][1]) s.push_back(3);
			if(s.size() >= 3) continue;
			if(s.size() == 0)return ;
			p[i][0] = ++tot , p[i][1] = ++tot;
			if(s.size() == 1) Node(p[i][1],p[i][0]);
			for(int j=0;j<s.size();j++)
				if(s[j] < 2) in[s[j]][max(L[i][1],mn[1])].push_back(p[i][j]),ot[s[j]][min(R[i][1],mx[1])].push_back(p[i][j]);
				else in[s[j]][max(L[i][0],mn[0])].push_back(p[i][j]),ot[s[j]][min(R[i][0],mx[0])].push_back(p[i][j]);
 		}
 		rep(t,0,3){
 			int up = 0 , dp = 0;
 			for(int i=mn[t<2];i<=mx[t<2];i++){
 				rep(j,0,in[t][i].size()-1)
 					Node(in[t][i][j],up),Node(dp,in[t][i][j]^1);
 				Node(++tot , up) , up = tot;
 				Node(dp , ++tot) , dp = tot;
				rep(j,0,ot[t][i].size()-1)
					Node(ot[t][i][j],dp),Node(up,ot[t][i][j]^1);  
			 }
		}
		for(int i=1;i<=tot;i++) if(!dfn[i])
			dfs(i);
		rep(i,1,n) if(cs[i].size() <= 2){
			int u = c[p[i][0]] < c[p[i][1]] ? cs[i][0] : cs[i][1];
			if(u < 2) lx[u] = max(lx[u] , L[i][1]) , rx[u]  =min(rx[u] , R[i][1]);
			else lx[u] = max(lx[u] , L[i][0]) , rx[u] = min(rx[u] , R[i][0]);
		}
		ans[0][0] = mn[0] , ans[0][1] = lx[0];
		ans[1][0] = mx[0] , ans[1][1] = lx[1];
		ans[2][1] = mn[1] , ans[2][0] = lx[2];
		ans[3][1] = mx[1] , ans[3][0] = lx[3];
		for(int i=0;i<K;i++)
			printf("%d %d\n",sx[ans[i][0]] , sy[ans[i][1]]);
	}
}

int main(){
	scanf("%d%d",&n,&K);
	for(int i=1;i<=n;i++) scanf("%d%d%d%d",&L[i][0],&L[i][1],&R[i][0],&R[i][1]),sx[++sx[0]]=L[i][0],sx[++sx[0]]=R[i][0],sy[++sy[0]]=L[i][1],sy[++sy[0]]=R[i][1];
	sort(sx+1,sx+1+sx[0]),sx[0]=unique(sx+1,sx+1+sx[0])-sx-1;
	sort(sy+1,sy+1+sy[0]),sy[0]=unique(sy+1,sy+1+sy[0])-sy-1;
	for(int i=1;i<=n;i++) L[i][0]=lower_bound(sx+1,sx+1+sx[0],L[i][0])-sx,R[i][0]=lower_bound(sx+1,sx+1+sx[0],R[i][0])-sx,
		L[i][1]=lower_bound(sy+1,sy+1+sy[0],L[i][1])-sy,R[i][1]=lower_bound(sy+1,sy+1+sy[0],R[i][1])-sy;
	for(int i=0;i<K;i++) ans[i][0] = ans[i][1] = 1;
	Solver1::Solve();
	if(K>=4) Solver2::Solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值