cf793g Oleg And Chess

传送门

网络流建图可以一眼看出来,重点在于线段树优化建图。
行记为前 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());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值