problem
有一个 n × n n\times n n×n 的棋盘,给出 q q q 个矩阵,这 q q q 个矩阵里不能放棋子,矩阵无交。
定义两颗棋子( ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 和 ( x 2 , y 2 ) (x_2,y_2) (x2,y2))冲突当且仅当 x 1 = x 2 x_1=x_2 x1=x2 或者 y 1 = y 2 y_1=y_2 y1=y2。
问棋盘中最多可以放多少互不冲突的棋子。
数据范围: 1 ≤ n ≤ 10000 1 ≤ n ≤ 10000 1 ≤ n ≤ 10000, 0 ≤ q ≤ 10000 0 ≤ q ≤ 10000 0 ≤ q ≤ 10000。
solution
网络流首先应该是不难看出来的。
如果一个格子能放棋子,我们就从它的行向列连边,最后二分图最大匹配就是答案。
但是如果暴力建边,边数是 O ( n 2 ) O(n^2) O(n2) 的,显然会 T。
优化方法?考虑线段树优化建图。
那么,如果是矩阵之内的格子能放,那直接用区间连区间的思路就可以了。
反过来,我们用扫描线,用线段树处理能放的区间,具体就是,我们一行一行扫,对于整个被矩形覆盖的区间就不连,如果没有被完全覆盖就连。
还是看代码吧,感觉有点口胡。。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)。
code
#include<bits/stdc++.h>
#define N 5000005
#define inf 0x3f3f3f3f
using namespace std;
int n,q,S,T,t=1,cnt,tot;
int first[N],v[N],w[N],nxt[N];
struct Line{int y1,y2,flag;};
vector<Line>l[N];
void add(int x,int y,int z){
nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=z;
nxt[++t]=first[y],first[y]=t,v[t]=x,w[t]=0;
}
namespace Segment_Tree{
int point[N],cov[N];
#define mid ((l+r)>>1)
void pushup(int root){
point[root]=++tot;
if(!cov[root<<1]) add(point[root],point[root<<1],inf);
if(!cov[root<<1|1]) add(point[root],point[root<<1|1],inf);
}
void build(int root,int l,int r){
if(l==r) {point[root]=n+l;return;}
build(root<<1,l,mid),build(root<<1|1,mid+1,r);
pushup(root);
}
void Modify(int root,int l,int r,int x,int y,int val){
if(l>=x&&r<=y) {cov[root]+=val;return;}
if(x<=mid) Modify(root<<1,l,mid,x,y,val);
if(y> mid) Modify(root<<1|1,mid+1,r,x,y,val);
pushup(root);
}
#undef mid
}
using namespace Segment_Tree;
namespace DINIC{
int d[N],f[N];
bool bfs(){
memset(d,-1,sizeof(d));
memcpy(f,first,sizeof(f));
queue<int>Q;Q.push(S),d[S]=0;
while(!Q.empty()){
int x=Q.front();Q.pop();
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(w[i]&&d[to]==-1) d[to]=d[x]+1,Q.push(to);
}
}
return d[T]!=-1;
}
int dinic(int now,int flow){
if(now==T) return flow;
int delta,ans=0;
for(int &i=f[now];i;i=nxt[i]){
int x=v[i];
if(w[i]&&d[x]==d[now]+1){
delta=dinic(x,min(flow,w[i]));
w[i]-=delta,w[i^1]+=delta,flow-=delta,ans+=delta;
if(!flow) return ans;
}
}
return ans;
}
}
using namespace DINIC;
int main(){
int x1,y1,x2,y2;
scanf("%d%d",&n,&q);
while(q--){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
l[x1].push_back((Line){y1,y2,1});
l[x2+1].push_back((Line){y1,y2,-1});
}
S=0,T=tot=2*n+1;
for(int i=1;i<=n;++i) add(S,i,1),add(n+i,T,1);
build(1,1,n);
for(int i=1;i<=n;++i){
for(int j=0;j<l[i].size();++j){
Line x=l[i][j];
Modify(1,1,n,x.y1,x.y2,x.flag);
}
if(!cov[1]) add(i,point[1],inf);
}
int ans=0;
while(bfs()) ans+=dinic(S,inf);
printf("%d\n",ans);
return 0;
}