P7216 [JOISC2020] 美味しい美味しいハンバーグ 题解

题意

N(2×105) 个矩形,要选出 K(4) 个点使得每个矩形内都有至少一个点。保证有解。

题解

神仙题,值得一想,不值得一写

从简单情况开始考虑。

K=1,直接求矩形交。

K=2,还是先直接求矩形交,若加入矩形 A 前交集不为空但加入 A 后交集为空,则存在已加入的矩形与 A 交为空。

这是为什么呢?考虑反证。注意到若三个矩形 A,B,C 满足 ABACA(BC)=,则 BC=

那么,若 ABACBC,则 A(BC)

因此,假设已加入的矩形都与 A 有交,又由于已加入的矩形两两间有交,故任意两个已加入的矩形的交与 A 有交,进而推出任意个已加入的矩形的交与 A 有交,矛盾!故存在已加入的矩形与 A 交为空。

故我们可以在 O(n) 的时间内找出一个与 A 无交的矩形 B,之后再从头加矩形。我们把只与 AB 有交的矩形加入 AB,并将 AB 与新加入的矩形取交集。由于保证有解,因此不存在与 A,B 无交的矩形,最后只剩一些与 A,B 都有交的矩形。此时只需任取一个矩形加入 A,B 中任意一个,再不断重复上述过程就可以了,证明还是考虑用上述结论反证,但较为繁琐,故略去不写。

但是该做法由于过度依赖分类讨论,因此很难扩展到 K=3,4,为了解决 K=3,4 的情况,需要注意到题目中的一个性质。

考虑最左的右边界,如果我们选的最左边的点在该右边界的左边,则将其横坐标调整到该右边界上来依然满足题意;如果我们选的最左边的点在该右边界的右边,则该右边界对应的矩形中不存在点,不符合题意。因此,存在一种合法方案使得最左边的右边界上一定有点。

同理,最右边的左边界、最上边的下边界、最下边的上边界上也都一定有点,由于 K=3 而我们要覆盖四个边界,由抽屉原理知四个边界的四个交点中至少有一个交点是要被选入答案的。

因此,枚举选哪个交点,之后就转成了 K=2 的问题。

对于 K=4,我们先运行一次上述做法,但是还有一种情况,四个边界上各有一个点且都不在角上。此时,若一个矩形与至少三条边界相交,则该矩形一定包含至少一条完整的边界,内部一定有点,可以不用考虑。若只与一条边界相交,则更新这条边界上可取点的范围。否则,与恰好两条边界相交,且在这两条边界上的交集中至少要有一条上有点。

考虑 2-SAT 建图,每个矩形建 4 个点 x,x,y,y 分别表示选交的第一条边界,不选交的第一条边界,选交的第二条边界,不选交的第二条边界,首先连边 xy,yx 表示两条边界至少要选一条与矩形有交。接着,若两个矩形 A,B 满足 A 的上边界比 B 的下边界还靠下,则连边 xBxAxAxB 表示 A,B 不可能选在同一条边界上(因为一条边界上只有一个点,而 A,B 与这条边界的交的交为空)。

对于第二类边,上下左右同理都要做一遍,连边需要按对应坐标排序后双指针来前后缀优化建图,这样子总点数和总边数便都缩小到了 O(n) 级别。

总时间复杂度为 O(n(B+logn)),其中 B=4。由于我比较菜,因此我写了 O(n(4K+logn)) 的做法。

Code

#include<bits/stdc++.h>
#define inf 1000000000
using namespace std;
int n,n2,k,tot,edge_t=0,L,D,R,U,las,t,dfn_t=0,rt_t=0,st_t=0,L1,D1,R1,U1;
int u[200002],one[16],la[2400002],id[200002],id2[200002],dfn[2400002],low[2400002],rt[2400002],st[2400002],to[200002],to1[200002];
typedef pair<int,int> P;
vector<P> vec;
struct aaa
{
	int l,d,r,u;
}p[200002];
struct bbb
{
	int to,nx;
}edge[5400002];
inline bool cmpl(int x,int y)
{
	return p[x].l<p[y].l;
}
inline bool cmpd(int x,int y)
{
	return p[x].d<p[y].d;
}
inline bool cmpr(int x,int y)
{
	return p[x].r<p[y].r;
}
inline bool cmpu(int x,int y)
{
	return p[x].u<p[y].u;
}
inline void add_edge(int x,int y)
{
	edge[++edge_t]=(bbb){y,la[x]},la[x]=edge_t;
}
inline void ins(int x,int y,int z)
{
	vec.push_back(P(x,y));
	for(int i=1;i<=n;++i)if(!u[i] && p[i].l<=x && p[i].d<=y && x<=p[i].r && y<=p[i].u)u[i]=z,--tot;
}
inline void clear(int x)
{
	vec.pop_back();
	for(int i=1;i<=n;++i)if(u[i]==x)u[i]=0,++tot;
}
inline bool dfs(int x)
{
	if(!tot)return 1;
	if(x>k)return 0;
	int L=1,D=1,R=inf,U=inf;
	for(int i=1;i<=n;++i)if(!u[i])L=max(L,p[i].l),D=max(D,p[i].d),R=min(R,p[i].r),U=min(U,p[i].u);
	if(x+1==k)
	{
		ins(L,D,x),ins(R,U,x+1);
		if(!tot)return 1;
		clear(x),clear(x+1);
		ins(R,D,x),ins(L,U,x+1);
		if(!tot)return 1;
		clear(x),clear(x+1);
		return 0;
	}
	ins(L,D,x);
	if(dfs(x+1))return 1;
	clear(x),ins(L,U,x);
	if(dfs(x+1))return 1;
	clear(x),ins(R,D,x);
	if(dfs(x+1))return 1;
	clear(x),ins(R,U,x);
	if(dfs(x+1))return 1;
	clear(x);
	return 0;
}
inline bool get(int x,int y)
{
	return u[x]&((1<<y)-1);
}
inline void Tarjan(int x)
{
	dfn[x]=low[x]=(++dfn_t),st[++st_t]=x;
	for(int i=la[x],v;i;i=edge[i].nx)
	{
		v=edge[i].to;
		if(!dfn[v])Tarjan(v),low[x]=min(low[x],low[v]);
		else if(!rt[v])low[x]=min(low[x],dfn[v]);
	}
	if(dfn[x]==low[x])
	{
		++rt_t;
		do rt[st[st_t]]=rt_t,--st_t;while(st[st_t+1]!=x);
	}
}
int main()
{
	scanf("%d%d",&n,&k),n2=(n<<1),tot=n;
	for(int i=1;i<=n;++i)scanf("%d%d%d%d",&p[i].l,&p[i].d,&p[i].r,&p[i].u);
	if(dfs(1))
	{
		for(int i=0;i<vec.size();++i)printf("%d %d\n",vec[i].first,vec[i].second);
		for(int i=vec.size();i<k;++i)puts("1 1");
		return 0;
	}
	L=1,D=1,R=inf,U=inf,tot=(n<<2);
	for(int i=1;i<16;++i)one[i]=one[i>>1]+(i&1);
	for(int i=1;i<=n;++i)L=max(L,p[i].l),D=max(D,p[i].d),R=min(R,p[i].r),U=min(U,p[i].u),id[i]=id2[i]=i;
	L1=R,D1=U,R1=L,U1=D;
	for(int i=1;i<=n;++i)
	{
		u[i]=0;
		if(p[i].l<=R && R<=p[i].r)u[i]|=1;
		if(p[i].l<=L && L<=p[i].r)u[i]|=2;
		if(p[i].d<=U && U<=p[i].u)u[i]|=4;
		if(p[i].d<=D && D<=p[i].u)u[i]|=8;
		if(one[u[i]]>2)continue;
		add_edge(i+n,i+n2),add_edge(i+n2+n,i);
		if(one[u[i]]==1)
		{
			add_edge(i+n2,i);
			if(u[i]==1)U1=min(U1,p[i].u);
			else if(u[i]==2)D1=max(D1,p[i].d);
			else if(u[i]==4)R1=min(R1,p[i].r);
			else L1=max(L1,p[i].l);
		}
	}
	sort(id+1,id+n+1,cmpu),sort(id2+1,id2+n+1,cmpd),las=0,t=1;
	for(int i=1;i<=n;++i)
	{
		to[id[i]]=id[i]+n2*get(id[i],0),to1[id[i]]=0;
		if(one[u[id[i]]]>2 || !(u[id[i]]&1))continue;
		add_edge(to1[id[i]]=(++tot),to[id[i]]+n);
		if(las)add_edge(tot,las);
		las=tot;
	}
	las=0;
	for(int i=1;i<=n;++i)
	{
		if(one[u[id2[i]]]>2 || !(u[id2[i]]&1))continue;
		while(t<=n && p[id[t]].u<p[id2[i]].d)
		{
			if(to1[id[t]])las=id[t];
			++t;
		}
		if(las)add_edge(to[id2[i]],to1[las]);
	}
	las=0,t=1;
	for(int i=1;i<=n;++i)
	{
		to[id[i]]=id[i]+n2*get(id[i],1),to1[id[i]]=0;
		if(one[u[id[i]]]>2 || !(u[id[i]]&2))continue;
		add_edge(to1[id[i]]=(++tot),to[id[i]]+n);
		if(las)add_edge(tot,las);
		las=tot;
	}
	las=0;
	for(int i=1;i<=n;++i)
	{
		if(one[u[id2[i]]]>2 || !(u[id2[i]]&2))continue;
		while(t<=n && p[id[t]].u<p[id2[i]].d)
		{
			if(to1[id[t]])las=id[t];
			++t;
		}
		if(las)add_edge(to[id2[i]],to1[las]);
	}
	for(int i=1;i<=n;++i)swap(id[i],id2[i]);
	las=0,t=n;
	for(int i=n;i;--i)
	{
		to[id[i]]=id[i]+n2*get(id[i],0),to1[id[i]]=0;
		if(one[u[id[i]]]>2 || !(u[id[i]]&1))continue;
		add_edge(to1[id[i]]=(++tot),to[id[i]]+n);
		if(las)add_edge(tot,las);
		las=tot;
	}
	las=0;
	for(int i=n;i;--i)
	{
		if(one[u[id2[i]]]>2 || !(u[id2[i]]&1))continue;
		while(t && p[id[t]].d>p[id2[i]].u)
		{
			if(to1[id[t]])las=id[t];
			--t;
		}
		if(las)add_edge(to[id2[i]],to1[las]);
	}
	las=0,t=n;
	for(int i=n;i;--i)
	{
		to[id[i]]=id[i]+n2*get(id[i],1),to1[id[i]]=0;
		if(one[u[id[i]]]>2 || !(u[id[i]]&2))continue;
		add_edge(to1[id[i]]=(++tot),to[id[i]]+n);
		if(las)add_edge(tot,las);
		las=tot;
	}
	las=0;
	for(int i=n;i;--i)
	{
		if(one[u[id2[i]]]>2 || !(u[id2[i]]&2))continue;
		while(t && p[id[t]].d>p[id2[i]].u)
		{
			if(to1[id[t]])las=id[t];
			--t;
		}
		if(las)add_edge(to[id2[i]],to1[las]);
	}
	sort(id+1,id+n+1,cmpr),sort(id2+1,id2+n+1,cmpl),las=0,t=1;
	for(int i=1;i<=n;++i)
	{
		to[id[i]]=id[i]+n2*get(id[i],2),to1[id[i]]=0;
		if(one[u[id[i]]]>2 || !(u[id[i]]&4))continue;
		add_edge(to1[id[i]]=(++tot),to[id[i]]+n);
		if(las)add_edge(tot,las);
		las=tot;
	}
	las=0;
	for(int i=1;i<=n;++i)
	{
		if(one[u[id2[i]]]>2 || !(u[id2[i]]&4))continue;
		while(t<=n && p[id[t]].r<p[id2[i]].l)
		{
			if(to1[id[t]])las=id[t];
			++t;
		}
		if(las)add_edge(to[id2[i]],to1[las]);
	}
	las=0,t=1;
	for(int i=1;i<=n;++i)
	{
		to[id[i]]=id[i]+n2*get(id[i],3),to1[id[i]]=0;
		if(one[u[id[i]]]>2 || !(u[id[i]]&8))continue;
		add_edge(to1[id[i]]=(++tot),to[id[i]]+n);
		if(las)add_edge(tot,las);
		las=tot;
	}
	las=0;
	for(int i=1;i<=n;++i)
	{
		if(one[u[id2[i]]]>2 || !(u[id2[i]]&8))continue;
		while(t<=n && p[id[t]].r<p[id2[i]].l)
		{
			if(to1[id[t]])las=id[t];
			++t;
		}
		if(las)add_edge(to[id2[i]],to1[las]);
	}
	for(int i=1;i<=n;++i)swap(id[i],id2[i]);
	las=0,t=n;
	for(int i=n;i;--i)
	{
		to[id[i]]=id[i]+n2*get(id[i],2),to1[id[i]]=0;
		if(one[u[id[i]]]>2 || !(u[id[i]]&4))continue;
		add_edge(to1[id[i]]=(++tot),to[id[i]]+n);
		if(las)add_edge(tot,las);
		las=tot;
	}
	las=0;
	for(int i=n;i;--i)
	{
		if(one[u[id2[i]]]>2 || !(u[id2[i]]&4))continue;
		while(t && p[id[t]].l>p[id2[i]].r)
		{
			if(to1[id[t]])las=id[t];
			--t;
		}
		if(las)add_edge(to[id2[i]],to1[las]);
	}
	las=0,t=n;
	for(int i=n;i;--i)
	{
		to[id[i]]=id[i]+n2*get(id[i],3),to1[id[i]]=0;
		if(one[u[id[i]]]>2 || !(u[id[i]]&8))continue;
		add_edge(to1[id[i]]=(++tot),to[id[i]]+n);
		if(las)add_edge(tot,las);
		las=tot;
	}
	las=0;
	for(int i=n;i;--i)
	{
		if(one[u[id2[i]]]>2 || !(u[id2[i]]&8))continue;
		while(t && p[id[t]].l>p[id2[i]].r)
		{
			if(to1[id[t]])las=id[t];
			--t;
		}
		if(las)add_edge(to[id2[i]],to1[las]);
	}
	for(int i=1;i<=(n2<<1);++i)if(!dfn[i])Tarjan(i);
	for(int i=1;i<=n;++i)
	{
		if(one[u[i]]!=2)continue;
		if(rt[i]<rt[i+n])
		{
			if(u[i]&1)U1=min(U1,p[i].u);
			else if(u[i]&2)D1=max(D1,p[i].d);
			else R1=min(R1,p[i].r);
		}
		else if(rt[i+n2]<rt[i+n2+n])
		{
			if(u[i]&8)L1=max(L1,p[i].l);
			else if(u[i]&4)R1=min(R1,p[i].r);
			else D1=max(D1,p[i].d);
		}
		else return 0&puts("Error");
	}
	return 0&printf("%d %d\n%d %d\n%d %d\n%d %d",R,U1,L,D1,R1,U,L1,D);
}
posted @   18Michael  阅读(62)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示