[NOI2008] 志愿者招募
\(Solution\)
这是一道一个人覆盖一个区间的问题,对于人,考虑把他看成一流量,对于\(a_i\)的限制,相当于把\(a_i\)的流量放在了\(i\)点上,所以\(S\)和\(1\)号点连\(inf\),\(n + 1\)号点和\(T\)连\(inf\),\(i\)和\(i + 1\)连\(inf - a_i\)。对于留下的流量一定要产生费用并且用完还要归还,所以对于一个人覆盖的区间,\(s_i\)和\(t_i + 1\)连一条\(inf\)流量,\(c_i\)费用的边,跑费用流即可。
\(Code\)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N = 120005,inf = 2e9;
int h[N],S,T,n,flow[N],vis[N],q[N * 10],tot = 1,pre[N],up[N],m;
LL dis[N];
struct edge{
int to,nxt,f,z;
}e[200005];
void add(int x,int y,int f,int z)
{
e[++tot] = edge{y,h[x],f,z},h[x] = tot;
e[++tot] = edge{x,h[y],0,-z},h[y] = tot;
}
int spfa()
{
memset(flow,127,sizeof flow);
memset(dis,50,sizeof dis);
memset(vis,0,sizeof vis);
int head = 0,tail = 1;
q[1] = S,dis[S] = 0,vis[S] = 1,pre[T] = -1;
while (head < tail)
{
int u = q[++head]; vis[u] = 0;
for (int i = h[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (dis[v] > dis[u] + (LL)e[i].z && e[i].f > 0)
{
dis[v] = dis[u] + (LL)e[i].z,pre[v] = u,up[v] = i;
flow[v] = min(flow[u],e[i].f);
if (!vis[v]) q[++tail] = v,vis[v] = 1;
}
}
}
return (pre[T] != -1);
}
int main()
{
scanf("%d%d",&n,&m);
S = n + 2,T = S + 1,add(S,1,inf,0),add(n + 1,T,inf,0);
for (int i = 1,q; i <= n; i++) scanf("%d",&q),add(i,i + 1,inf - q,0);
for (int i = 1,q,p,c; i <= m; i++)
scanf("%d%d%d",&q,&p,&c),add(q,p + 1,inf,c);
LL ansc = 0;
for (; spfa();)
{
int o = T; ansc += (LL)flow[T] * dis[T];
for (; o != S; o = pre[o]) e[up[o]].f -= flow[T],e[up[o] ^ 1].f += flow[T];
}
printf("%lld\n",ansc);
}