题解
发现就是一个线性规划。
我们用 (x_i) 来表示第 (i) 种志愿者的个数, (a_i) 来表示每一天的人数下限。
[x_o+x_p+...ge a_i\
x_o+x_q+...ge a_{i+1}\
...
]
就套路一下,把不等号变成等号。
[x_o+x_p+...=a_i+c_i\
x_o+x_q+...=a_{i+1}+c_{i+1}
]
然后可以做一下差,然后发现每一个 (x_o) 只会在等式的左右各出现一次,分别是 (s_a) 和 (t_a+1) 的位置。然后 (c_i) 也是只出现一次,然后就好了。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e3+5,M=1e4+5;
const int INF=1e18+7;
int n,m,from,to,tot=0;
int id[N],a[N];
struct Vol{int s,t,c;}b[M];
struct Edge{int nxt,to,flow,cost;};vector<Edge> e;int fir[N];
void add(int u,int v,int x,int y){e.push_back((Edge){fir[u],v,x,y}),fir[u]=e.size()-1;}
int dis[N],cur[N],res=0;
queue<int> q;bool vis[N];
bool spfa(){
for(int i=1;i<=tot;++i) dis[i]=INF,cur[i]=fir[i];
dis[from]=0,vis[from]=true,q.push(from);
while(!q.empty()){
int u=q.front();vis[u]=false,q.pop();
// printf("%d %d %d
",u,dis[u],fir[u]);
for(int i=fir[u];i>=0;i=e[i].nxt){
// printf("---%d %d %d
",e[i].to,e[i].cost,dis[e[i].to]);
if(!e[i].flow||dis[e[i].to]<=dis[u]+e[i].cost) continue;
dis[e[i].to]=dis[u]+e[i].cost;
if(!vis[e[i].to]) vis[e[i].to]=true,q.push(e[i].to);
}
}
return dis[to]!=INF;
}
int dfs(int u,int flow){
if(u==to) return flow;
int res=0;vis[u]=true;
for(int i=cur[u];i>=0&&flow;i=e[i].nxt){
int v=e[i].to;cur[u]=i;
if(!e[i].flow||dis[e[i].to]!=dis[u]+e[i].cost||vis[v]) continue;
int tmp=dfs(e[i].to,min(flow,e[i].flow));
e[i].flow-=tmp,e[i^1].flow+=tmp,flow-=tmp,res+=tmp;
}
return vis[u]=false,res;
}
signed main(){
cin>>n>>m;
from=++tot,to=++tot;
memset(fir,-1,sizeof(fir)),id[n+1]=++tot;
for(int i=1;i<=n;++i) scanf("%lld",&a[i]),id[i]=++tot;
for(int i=1;i<=m;++i) scanf("%lld%lld%lld",&b[i].s,&b[i].t,&b[i].c);
for(int i=1;i<=n;++i){
add(from,id[i+1],a[i],0),add(id[i+1],from,0,0);
add(id[i],to,a[i],0),add(to,id[i],0,0);
add(id[i],id[i+1],INF,0),add(id[i+1],id[i],0,0);
}
for(int i=1;i<=m;++i){
add(id[b[i].t+1],id[b[i].s],INF,b[i].c);
add(id[b[i].s],id[b[i].t+1],0,-b[i].c);
}
while(spfa()) res+=dis[to]*dfs(from,INF);
printf("%lld
",res);
return 0;
}