【网络流+线段树优化建图】CF793G Oleg and chess

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/84294894

【题目】
原题地址
有一个 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;
}

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/84294894
今日推荐