网络流建图可以一眼看出来,重点在于线段树优化建图。
行记为前
n
n
n个点,列记为第
n
+
1
n+1
n+1~
n
+
n
n+n
n+n个点。
直观的做法:一行一行地连。
于是现在用线段树优化某一行向某若干列的连边。
利用扫描线的思想,给不可选的矩形打上标记,每一行的覆盖用一个
v
e
c
t
o
r
vector
vector来存储。那么在从第一行往最后一行扫的时候,先把对应区间的标记打下去,然后对于每一个行号
i
i
i,向第
i
i
i行可选的若干区间连边,具体见线段树部分代码。这样连出来的边数应该是对的吧。。
然后如果这整个区间都不可选就不向线段树根节点连边,否则连一条边向线段树根节点。
注意每次
p
u
s
h
u
p
pushup
pushup的时候结点的编号是会变的。
然后跑个 d i n i c dinic dinic,再卡卡常就过了。主要是熟悉一下线段树优化建图。
更优的做法应该就是把那些矩形搞出来吧,大多题解都是这样做的,不过我懒。
#include<bits/stdc++.h>
#define cs const
#define re register
cs int N=2e4+10,M=3e6+10,oo=1e9;
int n,m,S,T,tot;
namespace IO{
cs int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
template<typename T>
inline T get(){
char ch;T x;
while(!isdigit(ch=gc()));x=ch^48;
while(isdigit(ch=gc())) x=((x+(x<<2))<<1)+(ch^48);
return x;
}
inline int gi(){return get<int>();}
}
using namespace IO;
namespace NETWORK_FLOW{
int Head[M],Next[M],V[M],W[M],cnt=1;
int dep[M],cur[M];
inline void bi_add(int u,int v,int w){
Next[++cnt]=Head[u],V[cnt]=v,W[cnt]=w,Head[u]=cnt;
Next[++cnt]=Head[v],V[cnt]=u,W[cnt]=0,Head[v]=cnt;
}
inline bool bfs(){
memset(dep,0,sizeof(dep));
std::queue<int> Q;Q.push(S);
dep[S]=1,cur[S]=Head[S];
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])
if(!dep[v]&&W[i]){
dep[v]=dep[u]+1,cur[v]=Head[v];
if(v==T) return 1;Q.push(v);
}
}return 0;
}
inline int dfs(int u,int dist){
if(u==T||!dist) return dist;
for(int &i=cur[u],v=V[i];i;v=V[i=Next[i]]){
if(dep[v]==dep[u]+1&&W[i]){
int d=dfs(v,std::min(dist,W[i]));
if(d>0){W[i]-=d,W[i^1]+=d;return d;}
}
}return 0;
}
inline int dinic(int ret=0){
while(bfs())
while(int D=dfs(S,oo))
ret+=D;
return ret;
}
}
using NETWORK_FLOW::bi_add;
namespace SGT{
#define lc (root<<1)
#define rc (root<<1|1)
int id[N<<2],tag[N<<2];
inline void pushup(int root){
id[root]=++tot;
if(!tag[lc]) bi_add(id[root],id[lc],oo);
if(!tag[rc]) bi_add(id[root],id[rc],oo);
}
inline void build(int root,int l,int r){
if(l==r) return void(id[root]=n+l);
int mid=l+r>>1;
build(lc,l,mid),build(rc,mid+1,r);
pushup(root);
}
inline void update(int root,int l,int r,int ql,int qr,int val){
if(ql<=l&&r<=qr) return void(tag[root]+=val);
int mid=l+r>>1;
if(ql<=mid) update(lc,l,mid,ql,qr,val);
if(qr> mid) update(rc,mid+1,r,ql,qr,val);
pushup(root);
}
#undef lc
#undef rc
}
using SGT::update;
struct node{int l,r,val;};
std::vector<node> V[N];
int main(){
n=gi(),m=gi(),tot=2*n+2;S=2*n+1;T=2*n+2;
for(int re i=1;i<=m;++i){
int x1=gi(),y1=gi(),x2=gi(),y2=gi();
V[x1].push_back((node){y1,y2,1});
V[x2+1].push_back((node){y1,y2,-1});
}
for(int re i=1;i<=n;++i)
bi_add(S,i,1),bi_add(i+n,T,1);
SGT::build(1,1,n);
for(int re i=1;i<=n;++i){
for(int re j=0;j<V[i].size();++j)
update(1,1,n,V[i][j].l,V[i][j].r,V[i][j].val);
if(!SGT::tag[1]) bi_add(i,SGT::id[1],oo);
}printf("%d\n",NETWORK_FLOW::dinic());
}