luogu P3980 [NOI2008] 志愿者招募
题面传送门
其实在链上不是很好吗,为什么要把它上树呢
容易想到网络流。
但是有一个大问题就是网络流没有办法表示加和。
其实如果是普通线性规划这个东西不应该连续起来。
但是它连续了就有了可以做的办法。
定义\((x,y,g,w)\)为\(x->y\)流量为\(g\),费用为\(w\)的边。
如果我们连\((ST,1,INF,0)\),再连\((n+1,T,INF,0)\),并将人数用负流表示,那么只要让其满流然后计算最小费用即可。
人数自然是连接在两天之间的\((i,i+1,INF-a_i,0)\)
然后考虑志愿者怎么连。
因为志愿者在第\(t_i\)天还能用所以应该连\((s_i,t_i+1,INF,c_i)\)
跑一遍KM即可。时间复杂度\(O(能过)\)
code:
#include <vector>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#include<set>
#include<map>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define l(x) x<<1
#define r(x) x<<1|1
#define re register
#define ll long long
#define db long double
#define N 1000
#define eps (1e-14)
#define mod 998244353
using namespace std;
int n,m,pre[N+5],st,t,x,y,z,now; ll ans,d[N+5],g[N+5];
struct yyy{int to;ll w,g;int z;}tmp;
struct ljb{
int head,h[N+5];yyy f[N+5<<6];
I void add(int x,int y,ll w,ll g){f[head]=(yyy){y,w,g,h[x]};h[x]=head++;}
}s;queue<int> q;
I void get(int x,int y,ll w,ll g){s.add(x,y,w,g);s.add(y,x,-w,0);}
I int bfs(){
re int i;memset(d,0x3f,sizeof(d));memset(g,0,sizeof(g));
d[st]=0;g[st]=1e18;q.push(st);while(!q.empty()){
now=q.front();q.pop();
for(i=s.h[now];~i;i=tmp.z){
tmp=s.f[i];if(tmp.g&&d[tmp.to]>d[now]+tmp.w){
d[tmp.to]=d[now]+tmp.w;g[tmp.to]=min(g[now],tmp.g);pre[tmp.to]=i;q.push(tmp.to);
}
}
}
return g[t];
}
int main(){
freopen("1.in","r",stdin);
re int i;scanf("%d%d",&n,&m);memset(s.h,-1,sizeof(s.h));st=0;t=n+2;get(st,1,0,1e14);get(n+1,t,0,1e14);
for(i=1;i<=n;i++) scanf("%d",&x),get(i,i+1,0,1e14-x);for(i=1;i<=m;i++) scanf("%d%d%d",&x,&y,&z),get(x,y+1,z,1e14);
while(bfs()){
now=t;ans+=d[t]*g[t];while(now^st)s.f[pre[now]].g-=g[t],s.f[pre[now]^1].g+=g[t],now=s.f[pre[now]^1].to;
}
printf("%lld\n",ans);
}