P3980 [NOI2008]志愿者招募

题解

发现就是一个线性规划。
我们用 \(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\n",u,dis[u],fir[u]);
		for(int i=fir[u];i>=0;i=e[i].nxt){
			// printf("---%d %d %d\n",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\n",res);
	return 0;
}
posted @ 2021-01-31 20:08  Point_King  阅读(61)  评论(0编辑  收藏  举报