【扫描线】【网络流】CodeForces793G Oleg and chess

题意:

给出一个棋盘,其中一些矩形位置不能放棋子。
现在往棋盘里面放入车,使得其不能互相攻击。
求能放的最大数量。


分析:

如果题目是给出:某些矩形能放棋子,那这题就非常的模板了。

可以把矩形当做点,然后每行每列分别视为一个点,行点向覆盖其的矩形连边,矩形再向覆盖的列点连边。原点向每个行点连一条容量为1的边,列点向汇点连一条容量为1的边。

然而这样显然会T,所以要用线段树优化建图。
在这里插入图片描述
嗯。。非常丑陋形象的图

然后,现在问题是,它给出的是不能放的矩形。

那么显然需要一种新的矩形划分方式,来得到能放的矩形:
每个矩形的上边和下边,分别向两侧延长,直到遇到被挡住或者边界为止。
在这里插入图片描述
这样理论上得到的矩形不会超过给出矩形的4倍。证明很容易,这里不再赘述。

为了得到这个矩形,有2种方法:
1、暴力扫描线(通常1500~2000ms左右,写得丑可能会更慢)
2、线段树扫描线(200ms以内)

但是线段树要多上接近70行左右。。。5.3KB的代码就这么诞生了

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 10010
#define INF 0x3FFFFFFF
using namespace std;
struct node{
	int l,r,h;
	bool flag;
	node () {}
	node (int l1,int r1,int h1,bool flag1):l(l1),r(r1),h(h1),flag(flag1) {}
	bool operator <(const node &a) const {
		if(h!=a.h)
			return h<a.h;
		return flag<a.flag;
	}
}lin[MAXN*2];
struct Tree{
	int h,tag,sum;	
}tree[MAXN*10];
int n,tot,s,t;
void pushdown(int id,int l,int r){
	if(tree[id].tag!=0){
		tree[id<<1].tag+=tree[id].tag;
		tree[id<<1|1].tag+=tree[id].tag;
		int mid=(l+r)>>1;
		tree[id<<1].sum+=(mid-l+1)*tree[id].tag;
		tree[id<<1|1].sum+=(r-mid)*tree[id].tag;
		tree[id].tag=0;
	}
}
void pushup(int id){
	tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;	
}
vector<int> a[MAXN*8],w[MAXN*8],rev[MAXN*8];
void add_edge(int u,int v,int len){
//	PF("[%d %d %d]\n",u,v,len);
	a[u].push_back(v);
	a[v].push_back(u);
	w[u].push_back(len);
	w[v].push_back(0);
	rev[u].push_back(a[v].size()-1);
	rev[v].push_back(a[u].size()-1);
}
int find_lft(int x,int id=1,int l=1,int r=n){
	if(tree[id].sum==0)
		return l-1;
	if(l==r)
		return l;
	pushdown(id,l,r);
	int mid=(l+r)>>1;
	if(x<=mid)
		return find_lft(x,id<<1,l,mid);
	else{
		if(tree[id<<1|1].sum==0)
			return find_lft(x,id<<1,l,mid);
		int res=find_lft(x,id<<1|1,mid+1,r);
		if(res==mid)
			return find_lft(x,id<<1,l,mid);
		return res;
	}
}
int find_rit(int x,int id=1,int l=1,int r=n){
	if(tree[id].sum==0)
		return r+1;
	if(l==r)
		return r;
	pushdown(id,l,r);
	int mid=(l+r)>>1;
	if(x<=mid){
		if(tree[id<<1].sum==0)
			return find_rit(x,id<<1|1,mid+1,r);
		int res=find_rit(x,id<<1,l,mid);
		if(res==mid+1)
			return find_rit(x,id<<1|1,mid+1,r);
		return res;
	} 
	else
		return find_rit(x,id<<1|1,mid+1,r);
} 
int pl[MAXN*10],pr[MAXN*10];
void build(int l,int r,int id,bool flag){
	if(l==r){
		if(flag==0)
			add_edge(s,id,1);
		else
			add_edge(id,t,1);
		return ;
	}
	int mid=(l+r)>>1;
	pl[id]=++tot;
	pr[id]=++tot;
	build(l,mid,pl[id],flag);
	build(mid+1,r,pr[id],flag);
	if(flag==0){
		add_edge(pl[id],id,INF);
		add_edge(pr[id],id,INF);
	}
	else{
		add_edge(id,pl[id],INF);
		add_edge(id,pr[id],INF);
	}
}
void add_edge_lc(int l1,int r1,int tar,int id,bool flag,int l=1,int r=n){
	if(l>=l1&&r<=r1){
		if(flag==0)
			add_edge(id,tar,INF);
		else
			add_edge(tar,id,INF);
		return ;
	}	
	int mid=(l+r)>>1;
	if(l1<=mid)
		add_edge_lc(l1,r1,tar,pl[id],flag,l,mid);
	if(r1>mid)
		add_edge_lc(l1,r1,tar,pr[id],flag,mid+1,r);
}
int rtc,rtr;
void add_blo(int l,int r,int lw,int up){
	if(lw==up)
		return ;
//	PF("--[%d %d %d %d]--\n",l,r,lw,up);
	tot++;
	add_edge_lc(up+1,lw,tot,rtc,0);
	add_edge_lc(l,r,tot,rtr,1);
}
void putline(int l1,int r1,int h,int id=1,int l=1,int r=n){
	if(l>=l1&&r<=r1){
		tree[id].h=max(tree[id].h,h);
		return ;
	}
	int mid=(l+r)>>1;
	if(l1<=mid)
		putline(l1,r1,h,id<<1,l,mid);
	if(r1>mid)
		putline(l1,r1,h,id<<1|1,mid+1,r);
}
int get_h(int pos,int id=1,int l=1,int r=n){
	if(l==r)
		return tree[id].h;
	int mid=(l+r)>>1;
	if(pos<=mid)
		return max(tree[id].h,get_h(pos,id<<1,l,mid));
	else
		return max(tree[id].h,get_h(pos,id<<1|1,mid+1,r));
}
void cov(int l1,int r1,int id=1,int l=1,int r=n){
	if(l!=r)
		pushdown(id,l,r);
	if(l>=l1&&r<=r1){
		tree[id].sum+=(r-l+1);
		tree[id].tag++;
		return ;
	}
	int mid=(l+r)>>1;
	if(l1<=mid)
		cov(l1,r1,id<<1,l,mid);
	if(r1>mid)
		cov(l1,r1,id<<1|1,mid+1,r);
	pushup(id);
}
void recov(int l1,int r1,int id=1,int l=1,int r=n){
	if(l!=r)
		pushdown(id,l,r);
	if(l>=l1&&r<=r1){
		tree[id].sum-=(r-l+1);
		tree[id].tag--;
		return ;
	}
	int mid=(l+r)>>1;
	if(l1<=mid)
		recov(l1,r1,id<<1,l,mid);
	if(r1>mid)
		recov(l1,r1,id<<1|1,mid+1,r);
	pushup(id);
}
int d[MAXN*8],g[MAXN*8];
int dfs(int x,int flw){
//	PF("<%d %d %d>\n",x,flw,d[x]);
	if(x==t)
		return flw;
	int flw1=flw;
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(w[x][i]!=0&&d[u]==d[x]-1){
			int f=dfs(u,min(flw,w[x][i]));
			w[x][i]-=f;
			w[u][rev[x][i]]+=f;
			flw-=f;
			if(flw==0)
				break;
//			if(d[s]==tot+1)
//				return flw1-flw;
		}
	}
	if(flw==flw1){
		if(g[d[x]]==1){
			d[s]=tot+1;
			return flw1-flw;
		}
		g[d[x]]--;
		int minh=tot;
		for(int i=0;i<int(a[x].size());i++)
			if(w[x][i]!=0)
				minh=min(minh,d[a[x][i]]);
		d[x]=minh+1;
		g[d[x]]++;
	}
	return flw1-flw;
}
int maxflow(){
	g[0]=tot;
	int ans=0;
	while(d[s]<tot)
		ans+=dfs(s,INF);
	return ans;
}
int q,xl,yl,xr,yr;
int main(){
	SF("%d%d",&n,&q);
	for(int i=1;i<=q;i++){
		SF("%d%d%d%d",&yl,&xl,&yr,&xr);
		lin[i]=node(xl,xr,yl-1,1);
		lin[i+q]=node(xl,xr,yr,0);
	}
	q*=2;
	lin[++q]=node(1,n,n,1);
	s=1;
	t=2;
	tot=2;
	rtc=++tot;
	build(1,n,rtc,0);
	rtr=++tot;
	build(1,n,rtr,1);
	sort(lin+1,lin+1+q);
	for(int i=1;i<=q;i++){
//		PF("<%d %d %d %d>\n",lin[i].l,lin[i].r,lin[i].h,lin[i].flag);
		if(lin[i].flag==0){
			int l=find_lft(lin[i].l-1)+1;
			int r=find_rit(lin[i].r+1)-1;
			if(l<lin[i].l)
				add_blo(l,lin[i].l-1,lin[i].h,get_h(l));
			if(r>lin[i].r)
				add_blo(lin[i].r+1,r,lin[i].h,get_h(r));
			recov(lin[i].l,lin[i].r);
			putline(min(l,lin[i].l),max(r,lin[i].r),lin[i].h);
		}
		else{
			int l=find_lft(lin[i].l)+1;
			int r=find_rit(lin[i].l)-1;
			if(l<=r)
				add_blo(l,r,lin[i].h,get_h(l));
			cov(lin[i].l,lin[i].r);
			putline(min(l,lin[i].l),max(r,lin[i].r),lin[i].h);
		}
	}
	PF("%d",maxflow());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值