洛谷P3357:最长k可重线段集问题(网络流)

解析

本题的建模方法有很多,我的做法是补集思想转化成志愿者招募然后按照那道题的做法直接做,看题解更多是采用的对于不冲突的线段首尾加边的做法。
在前一道最长k可重区间问题中这两种做法谈不上孰优孰劣,但本题中题解的做法在处理垂直线段时更加简单。

做法和上一题似乎很像,区别一是权值定义有变,二就是关键的垂直线段处理了。
按照题解的做法,直接特判讨论即可。
如果按照志愿者招募呢?
注意到,一条 x = c x=c x=c 的垂直线段和 ( l , c ) , ( c , r ) (l,c),(c,r) (l,c),(c,r) 都是不冲突的。
考虑如何表示。
对于不垂直的线段 ( l , r ) (l,r) (l,r),将其改为 ( 2 l + 1 , 2 r ) (2l+1,2r) (2l+1,2r)
对于垂直线段 x = c x=c x=c 将其改为 ( 2 c , 2 c + 1 ) (2c,2c+1) (2c,2c+1)
这样,在不破坏原来冲突性质的前提下,很好的实现我们的要求。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
inline ll read(){
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
const int N=3e5+100;
const int M=1e6+100;
const int inf=1e9;

int n,m,k;

int s,t,tot;
struct node{
	int to,nxt,cap;
	int w;
}p[N];
int fi[N],cnt,cur[N];
inline void addline(int x,int y,int c,int w){
	p[++cnt]=(node){y,fi[x],c,w};fi[x]=cnt;
	return;
}
inline void add(int x,int y,int c,int w){
	addline(x,y,c,w);addline(y,x,0,-w);
	return;
}

int dis[N];
int vis[N];
queue<int>q;
bool spfa(){
	fill(dis,dis+1+tot,inf);
	dis[s]=0;q.push(s);vis[s]=1;
	while(!q.empty()){
		int now=q.front();q.pop();
		vis[now]=0;
		for(int i=cur[now]=fi[now];~i;i=p[i].nxt){
			int to=p[i].to;
			if(dis[to]<=dis[now]+p[i].w||!p[i].cap) continue;
			dis[to]=dis[now]+p[i].w;
			if(!vis[to]) vis[to]=1,q.push(to);
		}
	}
	return dis[t]<inf;
}
int cost;
int flow;
int dfs(int x,int lim){
	if(vis[x]) return 0;
	if(x==t||!lim){
		cost+=lim*dis[t];
		return lim;
	}
	vis[x]=1;
	int res(0);
	for(int &i=cur[x];~i;i=p[i].nxt){
		int to=p[i].to;
		if(dis[to]!=dis[x]+p[i].w) continue;
		int add=dfs(to,min(lim,p[i].cap));
		res+=add;lim-=add;
		p[i].cap-=add;p[i^1].cap+=add;
		if(!lim) break;
	}
	if(!res) dis[x]=-1;
	vis[x]=0;
	return res;
}
void dinic(){
	flow=cost=0;int tmp(0);
	while(spfa()){
		while((tmp=dfs(s,inf))) flow+=tmp;
	}
	return;
}
int que[1050],l[1050],r[1050],sum[1050],num;
int val[1050],ans;
signed main(){
	#ifndef ONLINE_JUDGE
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	#endif
	memset(fi,-1,sizeof(fi));cnt=-1;
	n=read();k=read();
	for(int i=1;i<=n;i++){
		int a=read(),b=read(),c=read(),d=read();
		if(a>c){
			swap(a,c);swap(b,d);
		}		
		val[i]=floor(sqrt(1ll*(a-c)*(a-c)+1ll*(b-d)*(b-d)));ans+=val[i];
		a<<=1;c<<=1;
		if(a==c) c++;
		else a++;
		l[i]=a;r[i]=c;
		que[++num]=l[i];que[++num]=r[i];
	}
	sort(que+1,que+1+num);
	num=unique(que+1,que+1+num)-que-1;
	tot=num;s=++tot;t=++tot;
	add(s,1,inf,0);add(num,t,inf,0);
	for(int i=1;i<=n;i++){
		l[i]=lower_bound(que+1,que+1+num,l[i])-que;
		r[i]=lower_bound(que+1,que+1+num,r[i])-que;
		sum[l[i]]++;sum[r[i]]--;
		//printf("(%d %d)\n",l[i],r[i]);
		add(l[i],r[i],1,val[i]);
	}
	for(int i=1;i<num;i++){
		sum[i]+=sum[i-1];
		int o=max(sum[i]-k,0);
		//printf("i=%d sum=%d o=%d\n",i,sum[i],o);
		add(i,i+1,inf-o,0);
	}
	dinic();
	ans-=cost;
	printf("%d\n",ans);
	return 0;
}
/*
4 2
2 0 7 4
5 4 8 6
0 5 8 6
3 1 4 6
*/

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值