我终于学会打开机房的LOJ了!

description

LOJ3272
\(n(n<=2*10^5)\)个矩形,让你找\(k(k<=4)\)个点可以覆盖所有矩形(点可重复),输出一种方案。(保证有解)

Solution

可以注意到k很小。
从边界考虑。找到x=max(l[]),y=min(r[]),x=max(d[]),y=min(u[])的四条关键线。四条关键线围成了一个关键矩形(注意:只考虑边线,是空心的)。
每条关键线必须要被覆盖,此时脑海中yy出了很多种情况。容易发现如果不选择矩形端点只会有一种情况(每条关键线都会覆盖上一个点),k只能为4。反之面对k<4时肯定会选择端点。
又发现如果会选择关键矩形端点,是一个很好的限制。
就可以暴力搜索k层,每次枚举选择的端点。删掉这个端点所覆盖的矩形,k--,继续递归……
复杂度是:\(O(4^k*n)\)
此时k<4如果有解就肯定找到解了。
当k=4时,还会有不选关键矩形端点,每条边上选一个点的情况。
目前有两个限制:
1.每个关键矩形只能被覆盖一次->每个矩形与关键矩形交出来的线段(区间)中至少有一个被覆盖。
2.每条关键线上有且只能选择一个(线段上的)点。
发现如果把矩形与关键矩形交出来的线段(区间)当做状态的话,就是一个选or不选的2-SAT问题。
步骤为:

  • 预处理出每个线段(保存来自的矩形即所在的关键线)
  • (限制1)对来自同一个矩形的线段分类讨论:
    1.如果该线段包含完了所在的整个关键线段,直接忽略(因为它肯定会被覆盖到,而且至少交了三条关键线段非常费事)。
    2.如果这个矩形交了两个线段idx,idy.则至少选一个,!idx->idy,!idy->idx
    3.如果只交了一个线段idx,就必须选择这条线段,!idx->idx。
    4.一个都没交(显然无解,不过题面保证有解则不可能出现情况4)
  • (限制2)枚举每条关键线段上的线段(这里直接可以映射为1维区间)
    每个区间向该关键线上其它与之不交的区间满足两者中最多一个(就idx->!idy,idy->!idx)。
    当然需要前后缀优化建图
    先按r排序建一排往前连的虚拟点(pre[x]->idx')(pre[x]->pre[x-1]),每个点二分前面离它最近的y.r<x.l的y,idx->pre[y](这样y以前的节点都能连到了_
    再按l排序同理后缀优化建图……
  • 跑完Tarjan后找到了必须选择的线段。然后输出每条关键线段上必选线段的交上任意一点(我直接输出左端点了)
    ps.复杂度\(O(8*n)\)但常数巨大,不过冲1s还是可以的。

总结:写了很久,非常难写……
算我最近做过最恶心的图论题了……

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
const int M=N<<3;
static char buf[1000000],*p1=buf,*p2=buf,obuf[1000000],*p3=obuf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++
#define putchar(x) (p3-obuf<1000000)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
template<typename item>
inline void read(register item &x)
{
    x=0;register char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
}
struct node {int l,r,d,u;}a[N],A[N];
struct cross {int l,r,opt,id;}cr[5],rc[5][N];
bool cmpR(cross u,cross v) {return u.r<v.r;}
bool cmpL(cross u,cross v) {return u.l<v.l;}
int step,inf=1e9,n,K,pth[5][2],acnt;
bool flag=0;
void dfs() {
	if(flag)return;
	if(!acnt) {
		for(int i=1;i<step;i++) printf("%d %d\n",pth[i][0],pth[i][1]);
		for(int i=step;i<=K;i++) printf("%d %d\n",pth[step-1][0],pth[step-1][1]);
		flag=1;return;
	}
	if(step==K+1) {return;}
	int x[2],y[2];
	x[0]=0,x[1]=inf,y[0]=0,y[1]=inf;
	for(int i=1;i<=acnt;i++) {
		x[0]=max(x[0],A[i].l);x[1]=min(x[1],A[i].r);
		y[0]=max(y[0],A[i].d);y[1]=min(y[1],A[i].u);
	}
	node ta[acnt+1];
	int tca=acnt;for(int i=1;i<=acnt;i++)ta[i]=A[i];
	for(int u=0;u<=1;u++) for(int v=0;v<=1;v++) {
		pth[step][0]=x[u],pth[step][1]=y[v];
		acnt=0;for(int i=1;i<=tca;i++) {
			if((pth[step][0]<ta[i].l)||(pth[step][0]>ta[i].r)||(pth[step][1]<ta[i].d)||(pth[step][1]>ta[i].u))A[++acnt]=ta[i];
		}
		step++;dfs();
		step--;acnt=tca;for(int i=1;i<=acnt;i++)A[i]=ta[i];
	}
}
void solve1() {
	for(int i=1;i<=n;i++) A[++acnt]=a[i];
	step=1;dfs();
}
int nxt[M<<1],to[M<<1],head[M],ecnt,nd,_[M],pre[M],suf[M],tot[5];
bool mark[M],In_s[M];
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
int dfn[M],low[M],st[M],SCC,Bl[M],Time,tp;
void Tarjan(int u) {
	In_s[st[++tp]=u]=1;dfn[u]=low[u]=++Time;
	for(int i=head[u];i;i=nxt[i]) {
		int v=to[i];
		if(!dfn[v]) Tarjan(v),low[u]=min(low[u],low[v]);
		else if(In_s[v])low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]) {
		++SCC;int v;
		do {
			v=st[tp--];In_s[v]=0;Bl[v]=SCC;
		}while(u!=v);
	}
}
int Cq,x[2],y[2];
void fc_Chose() {
	for(int i=2;i<=Cq;i+=2) {
		if(Bl[i]<Bl[_[i]]) {mark[i]=1;}
	}
	for(int t=0;t<4;t++) {
		int up=tot[t];
		int _emo=-1;
		for(int i=1;i<=up;i++) {
			int x=rc[t][i].id;
			if(!mark[x])continue;
			if(_emo==-1) _emo=rc[t][i].l;
			else _emo=max(_emo,rc[t][i].l);
		}
		if(t<2) printf("%d %d\n",x[t],_emo);
		else printf("%d %d\n",_emo,y[t-2]);
	}
}
void Build() {
	x[0]=0,x[1]=inf,y[0]=0,y[1]=inf;
	for(int i=1;i<=n;i++) {
		x[0]=max(x[0],a[i].l);x[1]=min(x[1],a[i].r);
		y[0]=max(y[0],a[i].d);y[1]=min(y[1],a[i].u);
	}
	if(x[0]>x[1])swap(x[0],x[1]);if(y[0]>y[1])swap(y[0],y[1]);
	nd=1;
	for(int i=1;i<=n;i++) {
		bool flag=1;int cnt=0;
		for(int t=0;t<=1;t++) {
			if(a[i].l<=x[t]&&x[t]<=a[i].r) {
				int L=max(a[i].d,y[0]),R=min(a[i].u,y[1]);
				if(L==y[0]&&R==y[1]) {flag=0;break;}
				cr[++cnt].l=L;cr[cnt].r=R;cr[cnt].opt=t;
			}
			if(a[i].d<=y[t]&&y[t]<=a[i].u) {
				int L=max(a[i].l,x[0]),R=min(a[i].r,x[1]);
				if(L==x[0]&&R==x[1]) {flag=0;break;}
				cr[++cnt].l=L;cr[cnt].r=R;cr[cnt].opt=t+2;
			}
		}
		if(!flag)continue;
		if(cnt==2) {
			int op1=cr[1].opt,op2=cr[2].opt;
			cr[1].id=++nd;_[nd]=nd+1;++nd;
			cr[2].id=++nd;_[nd]=nd+1;++nd;
			add_edge(nd-2,nd-1);add_edge(nd,nd-3);		//at least one
			rc[op1][++tot[op1]]=cr[1];rc[op2][++tot[op2]]=cr[2];
		}
		else {
			int op1=cr[1].opt;
			cr[1].id=++nd;_[nd]=nd+1;++nd;
			add_edge(nd,nd-1);
			rc[op1][++tot[op1]]=cr[1];
		}
	}
	Cq=nd;
	for(int t=0;t<4;t++) {
		int up=tot[t];
		if(!up)continue;
		sort(rc[t]+1,rc[t]+up+1,cmpR);
		for(int i=1;i<=up;i++) {pre[i]=++nd;if(i>1)add_edge(pre[i],pre[i-1]);add_edge(pre[i],_[rc[t][i].id]);}
		int rmn=rc[t][1].r;
		for(int i=1;i<=up;i++) {
			if(rmn<rc[t][i].l) {
				int l=1,r=i-1,y=-1;
				while(l<=r) {
					int mid=(l+r)>>1;
					if(rc[t][mid].r<rc[t][i].l) {y=mid;l=mid+1;}
					else r=mid-1;
				}
				add_edge(rc[t][i].id,pre[y]);
			}
		}
		sort(rc[t]+1,rc[t]+up+1,cmpL);
		for(int i=up;i>=1;i--) {suf[i]=++nd;if(i<up)add_edge(suf[i],suf[i+1]);add_edge(suf[i],_[rc[t][i].id]);}
		int lmx=rc[t][up].l;
		for(int i=up;i>=1;i--) {
			if(lmx>rc[t][i].r) {
				int l=i+1,r=up,y=-1;
				while(l<=r) {
					int mid=(l+r)>>1;
					if(rc[t][mid].l>rc[t][i].r) {y=mid;r=mid-1;}
					else l=mid+1;
				}
				add_edge(rc[t][i].id,suf[y]);
			}
		}
	}
}
void solve2() {
	Build();
//	printf("!%d\n",nd);
	for(int i=2;i<=nd;i++) if(!dfn[i])Tarjan(i);
	fc_Chose();
}
int main() {
	read(n);read(K);
	for(int i=1;i<=n;i++) read(a[i].l),read(a[i].d),read(a[i].r),read(a[i].u);
	solve1();
	if(!flag)solve2();
//	else printf("!");
	return 0;
}