bzoj1061: [Noi2008]志愿者招募

Posted Achen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1061: [Noi2008]志愿者招募相关的知识,希望对你有一定的参考价值。

传送门

线性规划,最小费用最大流。做法同学姐的逛街计划。

设第i天要工作的志愿者为a[i][],第i类志愿者的数量为c[i],可以列出式子

c[a[1][1]] + c[a[1][2]] + ……<=A[1]

c[a[2][1]] + c[a[2][2]] + ……<=A[2]

……

c[a[n][1]] + c[a[n][2]] + ……<=A[n]

加上辅助变量y,前后加入两个0式:

0 = 0                                                               (1)

c[a[1][1]] + c[a[1][2]] + ……=A[1] + y[1]           (2)

c[a[2][1]] + c[a[2][2]] + ……=A[2] + y[2]           (3)

……

c[a[n][1]] + c[a[n][2]] + ……=A[n] + y[n]            (n+1)

0 = 0                                                                  (n+2)

差分:

c[ ] + c[ ] + ……=A[1] + y[1]                               (1)

c[ ] + c[ ] + ……=A[2] + y[2] - A[1] - y[1]            (2)

……

c[ ] + c[ ] + ……=A[n] + y[n] - A[n-1] - y[n-1]         (n)

c[ ] + c[ ] + ……=- A[n] - y[n]                               (n+1)

容易看出对于A和y移项之后左右各出现了一次。

而左边一的坨c,因为每种志愿者工作的时间是连续的一段l,r,那么差分后l+1~r都消去了c,只有l式出现 + c ,r+1式出现-c。

移项后c也左右各出现了一次。

于是用和之前一样的建图方法就好了。

技术分享图片
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
#define inf 1e18
const int N=100007;
typedef long long LL;
using namespace std;
int n,m,in[N]; 

template<typename T> void read(T &x) {
    char ch=getchar(); x=0; T f=1;
    while(ch!=-&&(ch<0||ch>9)) ch=getchar();
    if(ch==-) f=-1,ch=getchar();
    for(;ch>=0&&ch<=9;ch=getchar()) x=x*10+ch-0; x*=f;
}

struct edge {
    int u,v,nx;
    LL fl,cap,cost;
    edge(){}
    edge(int u,int v,LL fl,LL cap,LL cost,int nx):u(u),v(v),fl(fl),cap(cap),cost(cost),nx(nx){} 
}e[N]; 

int fir[N],ecnt=1;
void add(int u,int v,LL cap,LL cost) {
    e[++ecnt]=edge(u,v,0,cap,cost,fir[u]); fir[u]=ecnt;
    e[++ecnt]=edge(v,u,0,0,-cost,fir[v]); fir[v]=ecnt;
}

int vis[N],p[N];
LL dis[N];
queue<int>que;
int spfa(int s,int t) {
    for(int i=1;i<=n;i++) vis[i]=0,dis[i]=inf;
    que.push(s); dis[s]=0;
    while(!que.empty()) {
        int x=que.front();
        que.pop(); vis[x]=0;
        for(int i=fir[x];i;i=e[i].nx) 
        if(e[i].cap>e[i].fl) {
            int y=e[i].v;
            if(dis[y]>dis[x]+e[i].cost) {
                dis[y]=dis[x]+e[i].cost;
                p[y]=i; 
                if(!vis[y]) {
                    vis[y]=1;
                    que.push(y);
                }
            }
        }
    }
    return dis[t]!=inf;
}

LL calc(int s,int t) {
    LL fl=inf,res=0;
    for(int i=t;i!=s;i=e[p[i]].u) 
        fl=min(fl,e[p[i]].cap-e[p[i]].fl),
        res+=e[p[i]].cost;
    for(int i=t;i!=s;i=e[p[i]].u) 
        e[p[i]].fl+=fl,e[p[i]^1].fl-=fl;
    return fl*res;
} 

LL max_flow(int s,int t) {
    LL res=0;
    while(spfa(s,t)) 
        res+=calc(s,t);
    return res;
}

int main() {
    read(n); read(m);
    int s=n+2,t=s+1;
    for(int i=1;i<=n;i++) {
        int A; read(A);
        in[i]+=A; in[i+1]-=A;
    }
    for(int i=1;i<=n+1;i++) {
        if(in[i]>=0) add(s,i,in[i],0);
        else add(i,t,-in[i],0);
        if(i>1) add(i,i-1,inf,0);
    }
    for(int i=1;i<=m;i++) {
        int l,r,c;
        read(l); read(r); read(c);
        add(l,r+1,inf,c);
    }
    n=t;
    LL ans=max_flow(s,t);
    printf("%lld\n",ans);
    return 0;
}
View Code

 

以上是关于bzoj1061: [Noi2008]志愿者招募的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1061: [Noi2008]志愿者招募 [单纯形法]

bzoj1061: [Noi2008]志愿者招募

bzoj 1061: [Noi2008]志愿者招募

BZOJ1061:[NOI2008]志愿者招募——题解

bzoj 1061 [Noi2008]志愿者招募(数学模型,MCMF)

bzoj1061 [Noi2008]志愿者招募