[网络流24题] 洛谷P3357 最长k可重线段集问题 费用流

类似于最长k可重区间集问题。不同的地方在于把开区间改成了开线段。由于是到 x x x轴的投影,所以与前一题是一样的。但是要注意到垂直于 x x x轴的线段,如果直接相连会使得图中出现负环。
题解给出的处理方法是先把所有的横坐标扩大两倍,如果相等就让右边的 + 1 +1 +1,否则让左边的 − 1 -1 1,然后再离散化。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
namespace MCMF {
	const int N=1e4,M=2e5;
	int tot=-1,n,S,T; 
	int front[N],pre[N],lst[N];
	ll flow[N],dis[N];
	bool inq[N];
	struct edge{ int v,nxt;ll c,w; }e[2*M];
	void init() {
		tot=-1;
		for(int i=1;i<=n;i++) front[i]=-1;
	}
	void add(int u,int v,ll c,ll w) {
		e[++tot].v=v,e[tot].w=w,e[tot].c=c,e[tot].nxt=front[u],front[u]=tot;
		e[++tot].v=u,e[tot].w=-w,e[tot].c=0,e[tot].nxt=front[v],front[v]=tot;
	}
	void reset() {
		pre[T]=-1;
		for(int i=1;i<=n;i++) dis[i]=flow[i]=inf;
		for(int i=1;i<=n;i++) inq[i]=0;
	}
	bool spfa() {
		reset();
		queue<int> q;
		q.push(S);
		inq[S]=1;dis[S]=0;
		while(!q.empty()) {
			int u=q.front();
			q.pop();
			inq[u]=0;
			for(int i=front[u];i!=-1;i=e[i].nxt) {
				int v=e[i].v;
				if(e[i].c>0) {
					if(dis[v]>dis[u]+e[i].w) {
						dis[v]=dis[u]+e[i].w;
						flow[v]=min(flow[u],e[i].c);
						pre[v]=u,lst[v]=i;
						if(!inq[v]) {
							inq[v]=1;
							q.push(v);
						}
					}
				}
			}
		}
		return pre[T]!=-1?1:0; 
	}
	pair<ll,ll> solve() {
		ll maxflow=0,mincost=0;
		while(spfa()) {
			int cur=T;
			maxflow+=flow[T];
			mincost+=flow[T]*dis[T];
			while(cur!=S) {
				e[lst[cur]].c-=flow[T];
				e[lst[cur]^1].c+=flow[T];
				cur=pre[cur];
			}
		}
		return make_pair(maxflow,mincost);
	} 
} 
const int N=5e4;
struct line {
	int x1,y1,x2,y2,dis;
	ll sq(int x) { return 1LL*x*x; }
	int getdis() { return (int)sqrt(sq(x1-x2)+sq(y1-y2)); }
}a[N];
int b[2*N];
int tot=0;
int main() {
	int n,k;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) {
		scanf("%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);
		a[i].dis=a[i].getdis(); 
		a[i].x1*=2;
		a[i].x2*=2;
		if(a[i].x1==a[i].x2) a[i].x2++;
		else a[i].x1++;
		if(a[i].x1>a[i].x2) 
			swap(a[i].x1,a[i].x2);
		b[++tot]=a[i].x1;
		b[++tot]=a[i].x2;
	} 
	sort(b+1,b+1+tot);
	tot=unique(b+1,b+1+tot)-(b+1);
	int S=tot+1;
	int T=tot+2; 
	MCMF::n=tot+2;
	MCMF::S=tot+1;
	MCMF::T=tot+2;
	MCMF::init();
	MCMF::add(S,1,k,0);
	MCMF::add(tot,T,k,0);
	for(int i=1;i<tot;i++)
		MCMF::add(i,i+1,k,0);
	for(int i=1;i<=n;i++) {
		int l=lower_bound(b+1,b+1+tot,a[i].x1)-b;
		int r=lower_bound(b+1,b+1+tot,a[i].x2)-b;
		MCMF::add(l,r,1,-a[i].dis);
	}
	printf("%lld\n",-MCMF::solve().second);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值