[NOI2008] 志愿者招募
个人感觉建模方式比较奇怪,之前还没有做到过这样建模的题目。大开眼界。
第一个问题是如何让流分身。假如一个流只服务一个节点那么直接上费用流,如果一个流只服务两个节点那么多半是二分图的方式建模,但这道题里它一个流服务很多节点,而且这些节点的集合有大大的交集,这意味着我们必须想新方式。想到让一个流跨过那些节点,但是如何保证每个时间都可以被分配到足够多的志愿者呢,考虑每个节点往下一个节点连接一条容量为 \(-c\) 的边,这样一来一个志愿者团队边的流量只有大于等于区间内所有边的流量绝对值的最大值才能保证所有的时间点都有足够的志愿者。然后发现网络流中不支持流量上界是负数的做法,于是给每个边加上正无穷,整个图的最小费用不会变化,于是就可以做了。
#include<bits/stdc++.h>
//#define feyn
#define int long long
const int N=1510;
const int M=3e6;
const int maxn=1e15;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline int min(int s1,int s2){
return s1<s2?s1:s2;
}
struct edge{
int t,v1,v2,next;
}e[M];
int head[N],esum=1;
inline void adde(int fr,int to,int v1,int v2){
e[++esum]=(edge){to,v1,v2,head[fr]};head[fr]=esum;
}
inline void add(int fr,int to,int v1,int v2){
//printf("%lld %lld %lld %lld\n",fr,to,v1,v2);
adde(fr,to,v1,v2);adde(to,fr,0,-v2);
}
int m,n,a[N],ss,tt,cnt;
queue<int>q;
int dis[N];
bool inq[N],vis[N];
inline bool check(){
memset(dis,0x3f,sizeof(dis));
memset(inq,0,sizeof(inq));
memset(vis,0,sizeof(vis));
q.push(ss);inq[ss]=true;dis[ss]=0;
while(!q.empty()){
int wh=q.front();q.pop();inq[wh]=false;//printf("wh=%lld %lld\n",wh,dis[wh]);
for(int i=head[wh],th;i;i=e[i].next){
if(e[i].v1==0)continue;
int now=dis[wh]+e[i].v2;
if(now>=dis[th=e[i].t])continue;//printf("%lld->%lld\n",wh,th);
dis[th]=now;if(inq[th]==false)inq[th]=true,q.push(th);
}
}
//for(int i=1;i<=cnt;i++)printf("%lld ",dis[i]);printf("\n");
return dis[tt]<=maxn;
}
int cost;
inline int dinic(int wh,int val){
//printf("dinicing %lld %lld\n",wh,val);
if(wh==tt)return cost+=val*dis[wh],val;
int used=0;vis[wh]=true;
for(int i=head[wh],th;i;i=e[i].next){
if(e[i].v1==0||dis[th=e[i].t]!=dis[wh]+e[i].v2||vis[th])continue;
int now=dinic(th,min(e[i].v1,val));
if(now)used+=now,val-=now,e[i].v1-=now,e[i^1].v1+=now;
if(val==0)break;
}
return used;
}
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
#endif
int s1,s2,s3;
read(m);read(n);
ss=++cnt,tt=++cnt;
for(int i=1;i<=m+1;i++)a[i]=++cnt;
add(ss,a[1],maxn,0);
add(a[m+1],tt,maxn,0);
for(int i=1;i<=m;i++){
read(s1);add(a[i],a[i+1],maxn-s1,0);
}
for(int i=1;i<=n;i++){
read(s1);read(s2);read(s3);
add(a[s1],a[s2+1],maxn,s3);
}
//check();printf("val=%lld\n",dinic(ss,maxn));check();
//printf("working\n");
while(check())dinic(ss,maxn);
printf("%lld\n",cost);
return 0;
}
一如既往,万事胜意