1. 程式人生 > 其它 >「JOISC 2020 Day1」漢堡肉

「JOISC 2020 Day1」漢堡肉

我終於學會開啟機房的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;
}