GYM105139C Lili Likes Polygons

zyxawa發表於2024-07-28

記矩形的併為 \(P\),定義多邊形的大小為它的頂點個數 \(|P|\),其 \(90\)°的頂角為凸角,\(270\)°的頂角為凹角並記凹點構成的集合為 \(C\),稱豎直或水平在多邊形內部分割開矩形的線為割線,連線了兩個凹點的割線為好割線

貪心可以發現對於 \(P\) 的任意極小矩陣劃分,所有的割線至少有一端是 \(P\) 上的凹點,即存在一個端點 \(\in C\),否則一定可以合併此割線兩邊的矩形

對於任意一個 \(P\) 的極小矩形剖分 \(R\) 及關於 \(R\) 的不相交的好割線集合 \(N\)\(4|R|=|P|+2|C|-4|N|\),考慮利用內角和的變化證明,它原本的內角和等於 \(90\)°\(\times\) 凸點數\(+270\)°\(\times\) 凹點數\(=(|P|+2|C|)\times 90\)°,對於每一條 \(N\) 中的好割線將每個端點切為 \(90\)°與 \(180\)°兩部分,前者構成最終矩陣的內角後者構成一條邊,那麼增量為 \(-2 \times 180\)°,對於不在 \(N\) 中的割線 \(a=(x,y)\),若 \(x\in C \wedge y \notin C\),那麼它會將一條邊或割線分為兩個角從而與 \(x\) 帶來的負增量抵消,否則 \(y\) 一定被其它割線分割從而與 \(y \notin C\) 的情況相同

考慮最大化 \(|N|\),由於只有豎直與水平的割線會相交,將豎直的好割線看作二分圖左部點,水平的看作右部點,它們的交點看作它們之間的連邊,那麼所求轉化為此圖的最大獨立集,使用網路流求解即可,考慮求解 \(|P|\)\(|C|\),可以列舉每個四連通塊,若只有一個點被覆蓋則 \(|P| \leftarrow |P|+1\),至於兩個多邊形的一角重合的情況與 \(|C|\),欽定四聯通塊的右上角沒有被覆蓋並列舉其四個方向的被覆蓋情況即可,考慮如何建圖,將矩形離散化後可以使用二維陣列模擬,找出所有凹點座標後可以 \(\text{O}(|P|^2)\) 列舉並 \(\text{O}(k)\) 延展列舉另一個端點,由於每個凹點最多屬於一個水平與豎直割線,所以均攤複雜度正確,再列舉它們是否相交即可

#include<bits/stdc++.h>
using namespace std;
int k,h,w,s,t,n,m,ans,val,tot=1,flow,l[301],p[301],r[301],q[301],d[701][701],c[701][701],head[8001],now[8001],dep[8001],nex[800001],edge[800001],ver[800001];
basic_string <int> vx,vy;
basic_string <tuple<int,int,int,int>> ux,uy;
void add(int u,int v,int w){
	ver[++tot]=v,edge[tot]=w,nex[tot]=head[u],head[u]=tot;
	ver[++tot]=u,edge[tot]=0,nex[tot]=head[v],head[v]=tot;
}
int bfs(){
	queue <int> q;
	for(int i=s;i<=t;i++) dep[i]=0,now[i]=head[i];
	dep[s]=1,q.push(s);
	while(q.size()){
		int x=q.front();
		q.pop();
		for(int i=head[x];i;i=nex[i]){
			if(edge[i]&&!dep[ver[i]]){
				dep[ver[i]]=dep[x]+1;
				q.push(ver[i]);
				if(ver[i]==t) return 1;
			}
		}
	}
	return 0;
}
int dinic(int x,int flow){
	if(x==t) return flow;
	int rest=flow,k=0;
	for(int i=now[x];i&&rest;i=nex[i]){
		now[x]=i;
		if(edge[i]&&dep[ver[i]]==dep[x]+1){
			k=dinic(ver[i],min(edge[i],rest));
			if(!k) dep[ver[i]]=0;
			rest-=k;
			edge[i]-=k;
			edge[i^1]+=k;
		}
	}
	return flow-rest;
}
int main(){
	scanf("%d",&k);
	for(int i=1;i<=k;i++) scanf("%d%d%d%d",&l[i],&p[i],&r[i],&q[i]),vx+=l[i],vx+=++r[i],vy+=p[i],vy+=++q[i];
	sort(begin(vx),end(vx)),vx.erase(unique(begin(vx),end(vx)),end(vx)),h=vx.size();
	sort(begin(vy),end(vy)),vy.erase(unique(begin(vy),end(vy)),end(vy)),w=vy.size();
	for(int i=1;i<=k;i++){
		l[i]=lower_bound(vx.begin(),vx.end(),l[i])-vx.begin()+1;
		r[i]=lower_bound(vx.begin(),vx.end(),r[i])-vx.begin()+1;
		p[i]=lower_bound(vy.begin(),vy.end(),p[i])-vy.begin()+1;
		q[i]=lower_bound(vy.begin(),vy.end(),q[i])-vy.begin()+1;
		d[l[i]][p[i]]++,d[l[i]][q[i]]--,d[r[i]][p[i]]--,d[r[i]][q[i]]++;
	}
	for(int i=1;i<=h;i++) for(int j=1;j<=w;j++) d[i][j]+=d[i-1][j]+d[i][j-1]-d[i-1][j-1];
	for(int i=1;i<=h;i++) for(int j=1;j<=w;j++) d[i][j]=d[i][j]>0;
	for(int i=1;i<=h;i++){
		for(int j=1;j<=w;j++){
			int cnt=d[i][j]+d[i-1][j]+d[i][j-1]+d[i-1][j-1];
			if(cnt==3) c[i][j]=1;
			if(cnt==1) ans++;
			if(!d[i][j]&&d[i-1][j]&&d[i][j-1]) ans++;
			if(!d[i][j]&&d[i+1][j]&&d[i][j-1]) ans++;
			if(!d[i][j]&&d[i-1][j]&&d[i][j+1]) ans++;
			if(!d[i][j]&&d[i+1][j]&&d[i][j+1]) ans++;
			if(!d[i][j]&&d[i-1][j]&&d[i][j-1]&&d[i-1][j-1]) ans+=2;
			if(!d[i][j]&&d[i-1][j]&&d[i][j+1]&&d[i-1][j+1]) ans+=2;
			if(!d[i][j]&&d[i+1][j]&&d[i][j-1]&&d[i+1][j-1]) ans+=2;
			if(!d[i][j]&&d[i+1][j]&&d[i][j+1]&&d[i+1][j+1]) ans+=2;
		}
	}
	for(int i=1;i<=h;i++){
		for(int j=1;j<=w;j++){
			if(c[i][j]){
				if(d[i-1][j]&&d[i][j]){
					int k=j;
					while(d[i-1][k]&&d[i][k]) k++;
					if(c[i][k]) ux+={i,i,j,k};
				}
				if(d[i][j-1]&&d[i][j]){
					int k=i;
					while(d[k][j-1]&&d[k][j]) k++;
					if(c[k][j]) uy+={i,k,j,j};
				}
			}
		}
	}
	n=ux.size(),m=uy.size(),val=n+m,t=n+m+1;
	for(int i=1;i<=n;i++) add(s,i,1);
	for(int i=1;i<=m;i++) add(i+n,t,1);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			auto [a,b,c,d]=ux[i-1];
			auto [e,f,g,h]=uy[j-1];
			if(e<=a&&a<=f&&c<=g&&g<=d) add(i,j+n,1); 
		}
	}
	while(bfs()) while(flow=dinic(s,1e9)) val-=flow;
	printf("%d",(ans-val*4)/4);
	return 0;
}