1. 程式人生 > >【網路流+線段樹優化建圖】CF793G Oleg and chess

【網路流+線段樹優化建圖】CF793G Oleg and chess

【題目】
原題地址
有一個 n × n n\times n 的矩陣,每行每列至多能放一個棋子,另外有 m m

個矩形的區域不能放棋子,問最多能放多少個棋子。 n , m 1 0 5 n,m\leq 10^5

【解題思路】
根據套路,我們對每行每列分別建一個點,那麼這就是一個二分圖最大匹配。不過邊數是 O ( n 2 ) O(n^2)

級別的,於是我們可以用線段樹優化建圖。

現在考慮限制,由於限制讓區間不連續,我們先要讓區間“連續”,才能套用線段樹優化。

我們考慮分割出的一個個矩陣,實際上有一些矩形內部的分割線是不必要的,這些分割線可以合併在一起。於是我們按列考慮,用掃描線處理出所有矩形,不難發現矩形的個數是 O ( n ) O(n) 級別的。這樣我們只需要對每個矩陣新建一個節點,矩陣節點再對線段樹上對應節點連邊即可。

這樣建圖點數是 O ( n ) O(n) 的,邊數是 O ( n l o g 2 n ) O(nlog^2n) 的,因為每個矩陣節點向行線段樹連邊有 l o g log 條,這裡每條連邊代表的區間又會分別向列線段樹連邊 l o g log 條,就是兩個 l o g log 了。

這樣就優美地通過了這題,具體建圖詳見程式碼。

【參考程式碼】

#include<bits/stdc++.h>
using namespace std;

const int N=1e4+10,M=4e5+10,INF=1e9;
int n,m,rt1,rt2,cnt,S,T;

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

namespace Dinic
{
	int tot,head[M],dis[M],cur[M];
	queue<int>q;

	struct Tway{int v,w,nex;}e[M<<3];
	void init(){tot=1;}
	void add(int u,int v,int w)
	{
		e[++tot]=(Tway){v,w,head[u]};head[u]=tot;
		e[++tot]=(Tway){u,0,head[v]};head[v]=tot;
	}
	bool bfs()
	{
		for(int i=S;i<=T;++i) dis[i]=-1,cur[i]=head[i];
		dis[S]=0;q.push(S);
		while(!q.empty())
		{
			int x=q.front();q.pop();
			for(int i=head[x];i;i=e[i].nex)
			{
				int v=e[i].v;
				if(~dis[v] || !e[i].w) continue;
				dis[v]=dis[x]+1;q.push(v);
			}
		}
		return ~dis[T];
	}
	int dfs(int x,int flow)
	{
		if(x==T || !flow) return flow;
		int used=0,f;
		for(int &i=cur[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(dis[v]==dis[x]+1 && (f=dfs(v,min(e[i].w,flow-used))))
			{
				used+=f;e[i].w-=f;e[i^1].w+=f;
				if(used==flow) break;
			} 
		}
		return used;
	}
	int dinic()
	{
		int ret=0;
		while(bfs()) ret+=dfs(S,INF);
		return ret;
	}
};
using Dinic::init;
using Dinic::add;
using Dinic::dinic;

struct node
{
    int x,l,r,f;
    node(int x=0,int l=0,int r=0,int f=0):x(x),l(l),r(r),f(f){}
}c[N<<1];
bool cmp(const node&a,const node&b)
{
    if(a.x==b.x) return a.f>b.f;
    return a.x<b.x;
}

namespace Segment
{
	int ls[N<<3],rs[N<<3],a[N];

	void build(int &x,int y,int l,int r,int d)
	{
		x=++cnt;
		if(y){if(!d) add(x,y,INF); else add(y,x,INF);}
		if(l==r) return;
		int mid=(l+r)>>1;
		build(ls[x],x,l,mid,d);build(rs[x],x,mid+1,r,d);
	}
	void build2(int x,int l,int r,int y)
	{
		if(l==r)
		{
			if(!y) add(S,x,1); else add(x,T,1);
			return;
		}
		int mid=(l+r)>>1;
		build2(ls[x],l,mid,y);build2(rs[x],mid+1,r,y);
	}
	void query(int x,int l,int r,int L,int R,int y,bool d)
	{
		if(!x || l>R || r<L) return;
		if(L<=l && r<=R)
		{
			if(!d) add(x,y,INF); else add(y,x,INF);
			return;
		}
		int mid=(l+r)>>1;
		query(ls[x],l,mid,L,R,y,d);query(rs[x],mid+1,r,L,R,y,d);
	}
	void insert(int x,int l,int r)
	{
		int pos=-1,las=-1,y;
		for(int i=l;i<=r+1;++i)
		{
			if(i<=r) y=a[i]+1,a[i]=-1;
			if(y!=las || i==r+1)
			{
				if(~pos)
				{
					int z=++cnt;
					query(rt1,1,n,pos,i-1,z,0);query(rt2,1,n,las,x,z,1);
				}
				pos=i;las=y;
			}
		}
	}
	void update(int x,int l,int r){for(int i=l;i<=r;++i)a[i]=x;}
};
using Segment::update;
using Segment::insert;
using Segment::build;
using Segment::build2;

int main()
{
#ifndef ONLINE_JUDGE
	freopen("CF793G.in","r",stdin);
	freopen("CF793G.out","w",stdout);
#endif 
	init();n=read();m=read();
	build(rt1,0,1,n,0);build(rt2,0,1,n,1);
	for(int i=0;i<m;++i)
	{
		int xl=read(),yl=read(),xr=read(),yr=read();
		c[i<<1]=node(xl-1,yl,yr,0);c[i<<1|1]=node(xr,yl,yr,1);
	}
	sort(c,c+m*2,cmp);
	for(int i=0;i<m<<1;++i) 
	{	
		if(!c[i].f) insert(c[i].x,c[i].l,c[i].r);
		else update(c[i].x,c[i].l,c[i].r);
	}
	insert(n,1,n);
	T=++cnt;
	build2(rt1,1,n,S);build2(rt2,1,n,T);
	printf("%d\n",dinic());

	return 0;
}