这真真真是一道超超超巧妙的题
首先拿到这道题,我考虑假设每个人需要x[i]个,那么我们就可以n个如下式子:
a1*x[1]+a2*x[2]+...+am*x[m]>=day1num
a1'*x[1]+a2'*x[2]+......+am'*x[m]>=day2num
.......
其中若第i个人对某天有贡献则a[i]=1 否则 a[i]=0
然后我就跑偏了......
我考虑这是一个n维空间,那么这就相当于一个n维空间上的线性规划问题
然后就不知道怎么在n维空间上做线性规划.....
后来明白正解是网络流
以下正解:
样例:
3 3
2 3 4
1 2 2
2 3 5
3 3 2
我们考虑第i种人需要x[i]个,那么可以得到以下不等式:
x[1]>=2;
x[1]+x[2]>=3;
x[2]+x[3]>=4;
我们设变量y[i]满足:
0=0; 1
x[1]-y[1]=2; 2
x[1]+x[2]-y[2]=3; 3
x[2]+x[3]-y[3]=4; 4
0=0; 5
然后将2式-1式,3式-2式,4式-3式,5式-4式可得
x[1]-y[1]=2;
x[2]+y[1]-y[2]=1;
x[3]-x[1]+y[2]-y[3]=1
-x[2]-x[3]+y[3]=-4
观察这三个式子,巧妙的是这三个式子中,x[i],y[i]都出现了2次,且前面的系数为+1和-1
这个式子很想网络流的流量平衡吧
所以我们可以将这n+1个式子看成n+1个点,再建立源点和汇点s,t;
1.对于等式右边c大于0的点,我们将其与s连一条容量为c,距离(费用)为0的边
2.对于等式右边c小于0的点,我们将其与t连一条容量为-c,距离(费用)为0的边
3.对于每个x[i],连一条x[i]指向-x[i]的容量为inf,距离为cost[i]的边
4.对于每个y[i],连一条y[i]指向-y[i]的容量为inf,距离为0的边
然后拿这个图跑一跑s->t的费用流,所得的最小费用就是答案
那么为什么这么建图呢?我们逐条解释
对于1,2因为我们把每个式子看成了一个点,那么该点是流量平衡的(即该等式成立),那么左边代表流进流出,右边恒等于一常数c,那么这个c是哪里来的呢?因为是常数,所以当c>0我们可以是从源点流出来,流到该点的,所以恒定不变,不参与其它流的流动,所以与源点连边。c<0亦然
对于3,4我们考虑第i个人贡献的区间是[l,r],那么x[i]是在第l个等式出现的,-x[i]是在第r+1个等式出现的,那么当x[i]发生改变时,对这张图的影响只有x[i],-x[i]这两个点,所以将x[i]于-x[i]连一条边,容量是inf,因为第i个人可以选无限多个,距离(即花费)
为cost[i]。y[i]同理,只是距离变成0,因为y[i]是我们设的辅助值,没有实际费用
以下为AC代码:
/**************************************************************
Problem: 1061
User: syh0313
Language: C++
Result: Accepted
Time:1892 ms
Memory:18512 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cmath>
using
namespace
std;
const
long
long
inf=1000000000000;
const
int
maxe=40010;
const
int
maxd=1050;
int
n,m,ff[maxe],tt[maxe],to[maxe],nt[maxe],st[maxe],w[maxe];
int
pre[maxd],s,t,q[4000010],head,tail,topt;
long
long
vv[maxe],dis[maxd],inc[maxd],cap[maxe],ans,a[maxd];
bool
f[maxd];
void
add(
int
x,
int
y,
long
long
rong,
long
long
v)
{
to[topt]=y; nt[topt]=st[x]; st[x]=topt;
cap[topt]=rong; w[topt]=v; topt++;
to[topt]=x; nt[topt]=st[y]; st[y]=topt;
cap[topt]=0; w[topt]=-v; topt++;
}
bool
spfa()
{
for
(
int
i=0;i<=t;i++) dis[i]=inf,f[i]=0,pre[i]=-1;
dis[s]=0; inc[s]=inf; q[1]=s; f[s]=1; head=tail=1;
while
(head<=tail)
{
int
x=q[head++]; f[x]=0;
int
p=st[x];
while
(p!=-1)
{
if
(dis[to[p]]>dis[x]+w[p] && cap[p]>0)
{
dis[to[p]]=dis[x]+w[p];
inc[to[p]]=min(cap[p],inc[x]);
pre[to[p]]=p;
if
(!f[to[p]]) {f[to[p]]=1; q[++tail]=to[p];}
}
p=nt[p];
}
}
if
(dis[t]==inf)
return
0;
return
1;
}
int
main()
{
memset
(st,-1,
sizeof
st);
scanf
(
"%d%d"
,&n,&m);
for
(
int
i=1;i<=n;i++)
scanf
(
"%lld"
,&a[i]);
for
(
int
i=1;i<=m;i++)
scanf
(
"%d %d %lld"
,&ff[i],&tt[i],&vv[i]);
s=n+2; t=n+3;
for
(
int
i=1;i<=n+1;i++)
if
(a[i]-a[i-1]>=0) add(s,i,a[i]-a[i-1],0);
else
add(i,t,a[i-1]-a[i],0);
for
(
int
i=1;i<=n;i++) add(i+1,i,inf,0);
for
(
int
i=1;i<=m;i++) add(ff[i],tt[i]+1,inf,vv[i]);
while
(spfa())
{
ans+=dis[t]*inc[t];
//printf("kkkkk %lld %lld\n",dis[t],inc[t]);
int
se=pre[t];
while
(se!=-1)
{
cap[se]-=inc[t];
cap[se^1]+=inc[t];
se=pre[to[se^1]];
}
}
printf
(
"%lld\n"
,ans);
return
0;
}