【题解】P3358 最长k可重区间集问题

最长k可重区间集问题

题目描述

给定实直线 Ln 个开区间组成的集合 I,和一个正整数 k,试设计一个算法,从开区间集合 I 中选取出开区间集合 SI,使得在实直线 L 上的任意一点 xS 中包含 x 的开区间个数不超过 k,且 zS|z| 达到最大(|z| 表示开区间 z 的长度)。

这样的集合 S 称为开区间集合 I 的最长 k 可重区间集。zS|z| 称为最长 k 可重区间集的长度。

对于给定的开区间集合 I 和正整数 k,计算开区间集合 I 的最长 k 可重区间集的长度。

输入格式

输入的第一行有 2 个正整数 nk,分别表示开区间的个数和开区间的可重叠数。接下来的 n 行,每行有 2 个整数,表示开区间的左右端点坐标 l,r,数据保证 l<r

输出格式

输出最长 k 可重区间集的长度。

样例 #1

样例输入 #1

4 2
1 7
6 8
7 10
9 13

样例输出 #1

15

提示

对于 100% 的数据,1n5001k3

题解

考虑某点重叠不超过 k 和网络流中的流量限制非常相似,于是可以类似建图:

  • 离散化
  • 每个区间左端点向右端点连一条价值为 len ,流量为1的边
  • 离散化后的点每点向其右边的点连一条流量为k,价值为0的边.
  • 从原点释放k的流量

直接跑费用流即可。

此题启发我们网络流的题目思考每一条流的意义是什么,同时要注意找增广路的过程实际和反悔过程同时进行

点击查看代码
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}

const int N=5001,M=200001;
int head[N],to[M],fro[M],val[M],flo[M],tail=1;
int n,K,fl[N],va[N];
int s,t,cnt,ans;
int dig[N],tot,L[N],R[N],par[N],fr[N],fa[N];
bool inque[N];

inline void addlin(int x,int y,int a,int b){
	to[++tail]=y;
	fro[tail]=head[x];
	head[x]=tail;
	flo[tail]=a,val[tail]=b;
	to[++tail]=x;
	fro[tail]=head[y];
	head[y]=tail;
	flo[tail]=0,val[tail]=-b;
	return ;
}
bool spfa(int last){
	deque<int>p;
	for(int i=1;i<=cnt;i++)fl[i]=inque[i]=fr[i]=fa[i]=0,va[i]=-1;
	fl[s]=last,va[s]=0;
	p.push_back(s);
	while(!p.empty()){
		int u=p.front();p.pop_front();
		inque[u]=false;
		for(int k=head[u];k;k=fro[k]){
			int x=to[k];
			if(!flo[k]||va[x]>=va[u]+val[k])continue;
			va[x]=va[u]+val[k];
			fl[x]=min(fl[u],flo[k]);
			fr[x]=k,fa[x]=u;
			if(!inque[x])inque[x]=true,p.push_back(x); 
		}
	}
	return fl[t]!=0;
}

signed main(){
	n=rd(),K=rd();
	s=++cnt,t=++cnt;
	for(int i=1;i<=n;i++)L[i]=dig[++tot]=rd(),R[i]=dig[++tot]=rd();
	sort(dig+1,dig+1+tot);
	tot=unique(dig+1,dig+1+tot)-dig-1;
	for(int i=0;i<=tot;i++)par[i]=++cnt;
	for(int i=1;i<=tot;i++)addlin(par[i-1],par[i],K,0);
	addlin(s,par[0],K,0),addlin(par[tot],t,K,0);
	for(int i=1;i<=n;i++){
		int len=R[i]-L[i];
		L[i]=lower_bound(dig+1,dig+1+tot,L[i])-dig;
		R[i]=lower_bound(dig+1,dig+1+tot,R[i])-dig;
		addlin(par[L[i]],par[R[i]],1,len);
	}
	while(K&&spfa(K)){
		K-=fl[t];
		ans+=fl[t]*va[t];
		for(int u=t;u!=s;u=fa[u]){
			flo[fr[u]]-=fl[t];
			flo[fr[u]^1]+=fl[t];
		}
	}
	printf("%d",ans);
	return 0;
}
posted @   flywatre  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示