版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/84294894
【题目】
原题地址
有一个
的矩阵,每行每列至多能放一个棋子,另外有
个矩形的区域不能放棋子,问最多能放多少个棋子。
【解题思路】
根据套路,我们对每行每列分别建一个点,那么这就是一个二分图最大匹配。不过边数是
级别的,于是我们可以用线段树优化建图。
现在考虑限制,由于限制让区间不连续,我们先要让区间“连续”,才能套用线段树优化。
我们考虑分割出的一个个矩阵,实际上有一些矩形内部的分割线是不必要的,这些分割线可以合并在一起。于是我们按列考虑,用扫描线处理出所有矩形,不难发现矩形的个数是 级别的。这样我们只需要对每个矩阵新建一个节点,矩阵节点再对线段树上对应节点连边即可。
这样建图点数是 的,边数是 的,因为每个矩阵节点向行线段树连边有 条,这里每条连边代表的区间又会分别向列线段树连边 条,就是两个 了。
这样就优美地通过了这题,具体建图详见代码。
【参考代码】
#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;
}