题目分析
设
Si
S
i
为第
i
i
天可以工作的志愿者的种类集合,为第
j
j
种志愿者招募的个数,那么我们可以得到若干形如:
的式子。我们添加变量 yi y i 使得原式变成:
(∑j∈Sixj)−yi=ai
(
∑
j
∈
S
i
x
j
)
−
y
i
=
a
i
设该式为 Pi P i 。添加式子 P0:y0=0 P 0 : y 0 = 0 和 Pn+1:yn+1=0 P n + 1 : y n + 1 = 0 后,式子之间两两相减可得若干形如 Pi−1−Pi P i − 1 − P i 的式子,记 Ki=Pi−1−Pi K i = P i − 1 − P i 。并且将 Ki K i 种等号右边的常数全部移到左边。
可以发现,假如第 j j 种志愿者工作的某一段时间是,那么在 Ksj K s j 中, xj x j 取负号。在第 Ktj+1 K t j + 1 中, xj x j 取正号。所有这些式子中,变量 x x 和一定拥有相等数量的取正号的式子和取负号的式子。
将一个 K K 式子看做点,一个取负号的变量或是常数看做流出,一个取正号的变量或常数看做流入,那么我们的目标是流量平衡,也就是等号右边为0。由于每种志愿者有不同的费用,所以使用费用流解决该问题。
对于一个取负号的式子,要有一条容量为inf费用为0的边指向 yi y i 取正号的式子。也就是每个 i i 到之间有一条容量为inf的边。
对于常数,一种方法是若正则从源点流过来,若负则流到汇点去。当然你也可以从点 i i 到点之间流一条容量为 −ai − a i 费用为0的边(也就是将那条容量为 inf i n f 的边的容量减少)
对于一个 xi x i 取负号的式子( Ksj K s j ),向一个 xi x i 取正号的式子( Ktj+1 K t j + 1 ),连一条容量为inf费用为 ci c i 的边。
代码
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
typedef long long LL;
const int N=1005,M=1000005,inf=0x3f3f3f3f;
int n,m,tot=1,S,T,ans;
int a[N],h[N],ne[M],to[M],flow[M],w[M],l[N],r[N];
int dis[N],pre[N],liu[N],inq[N];
void add(int x,int y,int z,int c) {
to[++tot]=y,ne[tot]=h[x],h[x]=tot,flow[tot]=z,w[tot]=c;
to[++tot]=x,ne[tot]=h[y],h[y]=tot,flow[tot]=0,w[tot]=-c;
}
int bfs() {
for(RI i=S;i<=T;++i) dis[i]=0x3f3f3f3f,pre[i]=inq[i]=0;
queue<int> q;
dis[S]=0,liu[S]=inf,q.push(S);
while(!q.empty()) {
int x=q.front();q.pop(),inq[x]=0;
for(RI i=h[x];i;i=ne[i])
if(flow[i]>0&&dis[x]+w[i]<dis[to[i]]) {
dis[to[i]]=dis[x]+w[i],pre[to[i]]=i;
liu[to[i]]=min(flow[i],liu[x]);
if(!inq[to[i]]) inq[to[i]]=1,q.push(to[i]);
}
}
if(!pre[T]) return 0;
ans+=liu[T]*dis[T];int x=T;
while(x) {
int kl=pre[x];
flow[kl]-=liu[T],flow[kl^1]+=liu[T],x=to[kl^1];
}
return 1;
}
int main()
{
int x,num;
n=read(),m=read();
S=0,T=n+1;
add(S,1,inf,0);
for(RI i=1;i<=n;++i) add(i,i+1,inf-read(),0);
for(RI i=1;i<=m;++i) {
num=read();
for(RI j=1;j<=num;++j) l[j]=read(),r[j]=read();
x=read();
for(RI j=1;j<=num;++j) add(l[j],r[j]+1,inf,x);
}
while(bfs());
printf("%d\n",ans);
return 0;
}