网络流24题(二十一)

二十一、​​最长k可重区间集问题​

题目描述

给定实直线 \(\mathbf{L}\) 上 \(n\) 个开区间组成的集合 \(\mathbf{I}\),和一个正整数 \(k\),试设计一个算法,从开区间集合 \(\mathbf{I}\) 中选取出开区间集合 \(\mathbf{S}\subseteq\mathbf{I}\),使得在实直线 \(\mathbf{L}\) 上的任意一点 \(x\),\(\mathbf{S}\) 中包含 \(x\) 的开区间个数不超过 \(k\),且 \(\sum_{z\in\text{S}}\lvert z\rvert\) 达到最大(\(\lvert z\rvert\) 表示开区间 \(z\) 的长度)。

这样的集合 \(\mathbf{S}\) 称为开区间集合 \(\mathbf{I}\) 的最长 \(k\) 可重区间集。\(\sum_{z\in\text{S}}\lvert z\rvert\) 称为最长 \(k\) 可重区间集的长度。

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

输入格式

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

输出格式

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

题解

模型

最大权不相交路径、最大费用最大流

两种思路:

第一种

限制上限这种事情不难想到可以在源点或者汇点旁边再连接一条流量上限为\(k\)的边。

再继续顺着这个思路下去。我们需要把每一条边的。

我们可以把每一个区间看作一个点,每一个点表示从\(a\)到\(b\),假设区间之间有重叠那么它们将会分流,而如果它们之间没有重叠那么说明他们可以共用同一流量。这就仿佛电路图中的串联与并联一样,我们把串联的点连接在一起,而并联的不管。

跑一下最大费用最大流即可。

第二种

第一种方式的思考是把每一个区间都单独的拎出来考虑他们之间的相交关系。而第二种方式则完全相反,我们把所有区间端点都压在数轴上考虑。

想象一条从源点到终点的单向水流,这条水流相当于数轴,数轴上有很多点,那么水流只需要满足一件事——流量小于等于\(k\),就可以保证每一个点都不出现超过\(k\)次。

如果存在区间\((a,b)\),那么我们从\(a\)到\(b\)连接一条边,容量为1,费用为区间长度。因为跑的是最大费用最大流,所以只要它是合法的,它就会贪心的走向这条路。

建图

两种建图方式。

方法1

按左端点排序所有区间,把每个区间拆分看做两个顶点\(i,i+n\),建立附加源\(S\)汇\(T\),以及附加顶点\(S’\)。

  1. 连接\(S\)到\(S’\)一条容量为\(K\),费用为0的有向边。
  2. 从\(S’\)到每个\(i\)连接一条容量为1,费用为0的有向边。
  3. 从每个\(i+n\)到\(T\)连接一条容量为1,费用为0的有向边。
  4. 从每个顶点\(i\)到\(i+n\)连接一条容量为1,费用为负区间长度的有向边。
  5. 对于每个区间\(a\),与它右边的不相交的所有区间\(b\)各连一条容量为1,费用为0的有向边。

求最大费用最大流

方法2

  1. 离散化所有区间的端点,把每个端点看做一个顶点,建立附加源\(S\),汇\(T\)。
  2. \(S\)为0,\(T\)为最右边\(+1\)
  3. 从顶点\(i\)到顶点\(i+1\),连接一条容量为\(k\),费用为0的有向边。
  4. 对于每个区间\([l,r]\),从\(l\)对应的顶点\(i\)到\(r\)对应的顶点\(j\)连接一条容量为\(1\),费用为负区间长度的有向边。

求最大费用最大流,最大费用流值就是最长k可重区间集的长度。

具体建图就会发现,后一种模型的边数更少,第一种上线是\(O(n^2)\),而后一种是\(O(n)\)

代码

第一种:
using namespace std;
#define lowbit(x) x&-x
#define ll long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a/gcd(a,b)*b
const double eps = 1e-6;
const ll mod = 1e9+7;
const ll inf = 1e9+50;
const ll N = 1e3+50,M = 5e4+50;

struct Edge{
ll to,w,cost,nxt;
}edge[M*2];
ll head[N],cnt = 1;
void add(ll u,ll v,ll w,ll c){
edge[++cnt] = {v,w,c,head[u]};
head[u] = cnt;
}
void add2(ll u,ll v,ll w,ll cost){
add(u,v,w,cost);
add(v,u,0,-cost);
}
ll s,t,dis[N],cur[N];
bool inq[N],vis[N];
queue<ll>q;
bool spfa(){
while(!q.empty())q.pop();
copy(head,head+N,cur);
fill(dis,dis+N,inf);
dis[s] = 0;q.push(s);
while(!q.empty()){
ll p = q.front();q.pop();
inq[p] = false;
for(ll e = head[p];e;e = edge[e].nxt){
ll to = edge[e].to,vol = edge[e].w;
if(vol > 0 && dis[to] > dis[p]+edge[e].cost){
dis[to] = dis[p] + edge[e].cost;
if(inq[to])continue;
q.push(to);
inq[to] = true;
}
}
}
return dis[t] != inf;
}
ll dfs(ll p = s,ll flow = inf){
if(p == t)return flow;
vis[p] = true;
ll rmn = flow;
for(ll eg = cur[p];eg&&rmn;eg = edge[eg].nxt){
cur[p] = eg;
ll to = edge[eg].to,vol = edge[eg].w;
if(vol > 0 && !vis[to] && dis[to] == dis[p]+edge[eg].cost){
ll c = dfs(to,min(vol,rmn));
rmn -= c;
edge[eg].w-=c;
edge[eg^1].w+=c;
}
}
vis[p] = false;
return flow-rmn;
}
ll maxflow = 0,mincost = 0;
void dinic(){
while(spfa()){
ll flow = dfs();
maxflow += flow;
mincost += dis[t]*flow;
}
}
pair<ll,ll> seg[N];
#define fir first
#define sec second
int main(){
ios::sync_with_stdio(false);
ll n,k;cin>>n>>k;
ll s0 = 2*n+1;
s = 0,t = 2*n+2;
add2(s,s0,k,0);
for(ll i = 1;i <= n;i++){
cin>>seg[i].fir>>seg[i].sec;
add2(s0,i,1,0);
add2(i+n,t,1,0);
ll len = seg[i].sec-seg[i].fir;
add2(i,i+n,1,-len);
}
for(ll i = 1;i <= n;i++){
for(ll j = i+1;j <= n;j++){
if(seg[i].sec <= seg[j].fir){
add2(i+n,j,inf,0);
}
if(seg[i].fir >= seg[j].sec){
add2(j+n,i,inf,0);
}
}
}
dinic();
cout<<-mincost<<endl;
return 0;
}
第二种
#define lowbit(x) x&-x
#define ll long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a/gcd(a,b)*b
const double eps = 1e-6;
const ll mod = 1e9+7;
const ll inf = 1e9+50;
const ll N = 1e3+50,M = 5e4+50;

struct Edge{
ll to,w,cost,nxt;
}edge[M*2];
ll head[N],cnt = 1;
void add(ll u,ll v,ll w,ll c){
edge[++cnt] = {v,w,c,head[u]};
head[u] = cnt;
}
void add2(ll u,ll v,ll w,ll cost){
add(u,v,w,cost);
add(v,u,0,-cost);
}
ll s,t,dis[N],cur[N];
bool inq[N],vis[N];
queue<ll>q;
bool spfa(){
while(!q.empty())q.pop();
copy(head,head+N,cur);
fill(dis,dis+N,inf);
dis[s] = 0;q.push(s);
while(!q.empty()){
ll p = q.front();q.pop();
inq[p] = false;
for(ll e = head[p];e;e = edge[e].nxt){
ll to = edge[e].to,vol = edge[e].w;
if(vol > 0 && dis[to] > dis[p]+edge[e].cost){
dis[to] = dis[p] + edge[e].cost;
if(inq[to])continue;
q.push(to);
inq[to] = true;
}
}
}
return dis[t] != inf;
}
ll dfs(ll p = s,ll flow = inf){
if(p == t)return flow;
vis[p] = true;
ll rmn = flow;
for(ll eg = cur[p];eg&&rmn;eg = edge[eg].nxt){
cur[p] = eg;
ll to = edge[eg].to,vol = edge[eg].w;
if(vol > 0 && !vis[to] && dis[to] == dis[p]+edge[eg].cost){
ll c = dfs(to,min(vol,rmn));
rmn -= c;
edge[eg].w-=c;
edge[eg^1].w+=c;
}
}
vis[p] = false;
return flow-rmn;
}
ll maxflow = 0,mincost = 0;
void dinic(){
while(spfa()){
ll flow = dfs();
maxflow += flow;
mincost += dis[t]*flow;
}
}
pair<ll,ll> seg[N];
ll num[N];
int main(){
ios::sync_with_stdio(false);
ll n,k;cin>>n>>k;
for(ll i = 1;i <= n;i++){
cin>>seg[i].first>>seg[i].second;
num[i] = seg[i].first;
num[i+n] = seg[i+n].second;
}
sort(num+1,num+n*2+1);
ll tot = unique(num+1,num+2*n+1)-num-1;
s = 0,t = tot+1;
for(ll i = 0;i <= tot;i++)add2(i,i+1,k,0);
for(ll i = 1;i <= n;i++){
ll p1 = lower_bound(num+1,num+1+tot,seg[i].first)-num-1;
ll p2 = lower_bound(num+1,num+1+tot,seg[i].second)-num-1;
add2(p1,p2,1,seg[i].first-seg[i].second);
}
dinic();
cout<<-mincost<<endl;
return 0;
}