题目大意:
在一段区间中存在一些线段
要求从中选出一些线段,使得线段每个点被线段覆盖次数不超过\(k\)次,且线段长度最长
最大费用最大流
我们可以由\(S\)向区间的起点连边,终点向\(T\)连边,容量为\(k\),费用为\(0\),保证了每个点被经过不超过\(k\)次
每个点向下一个点连边,容量\(k\),花费\(0\)
每个区间左断点向右端点连边,容量\(1\),花费为区间长度
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;
char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') f=0,ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
}
const int inf=0x3f3f3f3f;
int n,k,ret,st,ed;
int tx[1010],ty[1010],s[1010];
int head[100010],cnt=1;
struct point
{
int nxt,to,c,val;
}a[200010];
inline void add(int x,int y,int c,int val)
{
a[++cnt].nxt=head[x];
a[cnt].to=y;
a[cnt].c=c;
a[cnt].val=val;
head[x]=cnt;
}
inline void adds(int x,int y,int c,int val)
{
add(x,y,c,val);add(y,x,0,-val);
}
queue<int> q;
int dis[100010],c[100010],pre[100010],eg[100010];
bool vis[100010];
inline bool spfa()
{
memset(c,0x3f,sizeof(c));
memset(dis,-0x3f,sizeof(dis));dis[st]=0;
memset(vis,0,sizeof(vis));vis[st]=1;
q.push(st);pre[ed]=0;
while(!q.empty())
{
int now=q.front();
q.pop();
vis[now]=0;
for(int i=head[now];i;i=a[i].nxt)
{
int t=a[i].to;
if(a[i].c&&dis[t]<dis[now]+a[i].val)
{
pre[t]=now;
eg[t]=i;
dis[t]=dis[now]+a[i].val;
c[t]=min(c[now],a[i].c);
if(!vis[t])
{
vis[t]=1;
q.push(t);
}
}
}
}
return pre[ed];
}
inline int dinic()
{
while(spfa())
{
ret+=dis[ed]*c[ed];
int now=ed;
while(now^st)
{
a[eg[now]].c-=c[ed];
a[eg[now]^1].c+=c[ed];
now=pre[now];
}
}
return ret;
}
signed main()
{
n=read(),k=read();
for(int i=1;i<=n;++i)
{
tx[i]=read(),ty[i]=read();
s[++s[0]]=tx[i],s[++s[0]]=ty[i];
}
sort(s+1,s+s[0]+1);
s[0]=unique(s+1,s+s[0]+1)-s-1;//离散
st=s[0]+1,ed=s[0]+2;
for(int i=1;i<s[0];++i) adds(i,i+1,k,0);//每个点向右边连0边
adds(st,1,k,0);adds(s[0],ed,k,0);//起点终点,k个流限制每个点走k次
for(int x,y,i=1;i<=n;++i)
{
x=lower_bound(s+1,s+s[0]+1,tx[i])-s;
y=lower_bound(s+1,s+s[0]+1,ty[i])-s;
adds(x,y,1,ty[i]-tx[i]);//走线段
}
printf("%d\n",dinic());//最大费用最大流
return 0;
}