1. 程式人生 > >BZOJ1061-[Noi2008]誌願者招募

BZOJ1061-[Noi2008]誌願者招募

r+ bsp 誌願 printf pre scan flow amp space

BZOJ1061-[Noi2008]誌願者招募

  題意:

    技術分享

   題解:

      這題解法是費用流.第一眼我看到題解的時候,我想:這TM和費用流有啥關系!然後看完之後,我的內心:還有這種操作?

      我們設第i個誌願者招x[i]個,則我們可以得到一組不等式:

          sum(x[j])](s[j]<=j<=t[j])>=a[i];

      然後添加進去幾個變量:

          sum(x[j])(s[j]<=i<=t[j])-y[i]==a[i]

      顯然,所有x和y都大於等於0.

      我們將與a[i]有關的式子減去與a[i-1]有關的式子,得到:

          sum(x[j])(s[j]<=i<=t[j])-sum(x[j])(s[j]<=i-1<=t[j])+y[i]-y[i-1]==a[i]-a[i-1]

      如果一個x[j]在該式子中系數不為0,則意味著i-1在[s[j],t[j]]內且i不在[s[j],t[j]]內,或i-1不在[s[j],t[j]]內且i在[s[j],t[j]]內.對於第一種情況,i=t[j]+1且x[j]的系數為-1,對於第二種情況,i=s[j],且x[j]的系數為1.

      那麽對於所有x和y,它們都在這些式子都剛好出現了兩次,並且一次系數為1,一次系數為-1.

      然後,我們就(腦洞大開)地想到了網絡流!我們把一個式子看成一個點,每個變量看成一條邊,流量為inf,費用的話,如果是x,為代表的誌願者的費用,如果是y,為0.對於每一條變量代表的邊,從它為正的式子連向它為負的式子.並且對於式子右邊的常數(a[i]-a[i-1]),如果為正,從源點向該式子連一條容量為a[i]-a[i-1],費用為0的邊,否則從該式子向匯點連一條容量為a[i-1]-a[i],費用為0的邊,然後跑一遍費用流,就得到了答案,並且每條邊的流量就是該變量的取值.

      那麽為什麽這樣是對的呢?這個建圖法滿足了網絡流的一些性質.對於每條邊,流量非負,而式子中所有變量都有非負限制.對於每個點,流入的流量等於流出的流量,而按照這種建圖法,每個式子和其代表的點的限制其實是一樣的.所以這樣就是對的了.

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1000,maxm=10000;
const ll inf=0x3f3f3f3f;
struct edge{int to,nxt; ll cap,flow,val;}eg[maxm*4+10];
int h[maxn+10],ned[maxn+10],eg_cnt,n,m;
void add_edge(int u,int v,int cap,int val){
    eg[eg_cnt].to=v; eg[eg_cnt].cap=cap; eg[eg_cnt].val=val;
    eg[eg_cnt].nxt=h[u]; h[u]=eg_cnt++;
}
void add_edge_2(int u,int v,int cap,int val){
    add_edge(u,v,cap,val); add_edge(v,u,0,-val);
}
ll a[maxn+10],flow[maxn+10],pre[maxn+10][2],d[maxn+10]; bool vis[maxn+10];
queue<int> Q;
ll mcmf(){
    ll ans=0;
    for(;;){
        memset(flow,-1,sizeof flow); memset(d,0x3f,sizeof d); memset(vis,0,sizeof vis);
        d[1]=0; flow[1]=inf; vis[1]=1; Q.push(1); memset(pre,-1,sizeof pre);
        for(;!Q.empty();Q.pop()){
            int x=Q.front(); vis[x]=0;
            for(int i=h[x];i!=-1;i=eg[i].nxt)
                if(eg[i].flow<eg[i].cap&&d[x]+eg[i].val<d[eg[i].to]){
                    d[eg[i].to]=d[x]+eg[i].val; flow[eg[i].to]=min(flow[x],eg[i].cap-eg[i].flow);
                    pre[eg[i].to][0]=x; pre[eg[i].to][1]=i;
                    if(!vis[eg[i].to]){
                        vis[eg[i].to]=1; Q.push(eg[i].to);
                    }
                }
        }
        if(flow[2]==-1) break; ans+=flow[2]*d[2];
        for(int i=2;pre[i][0]!=-1;i=pre[i][0]){
            eg[pre[i][1]].flow+=flow[2];
            eg[pre[i][1]^1].flow-=flow[2];
        }
    }
    return ans;
}
int main(){
    scanf("%d%d",&n,&m); memset(h,-1,sizeof h);
    for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
    for(int i=1;i<=n+1;++i){
        if(a[i]-a[i-1]>=0) add_edge_2(1,i+2,a[i]-a[i-1],0);
        else add_edge_2(i+2,2,a[i-1]-a[i],0);
    }
    for(int i=1;i<=m;++i){
        int l,r,v; scanf("%d%d%d",&l,&r,&v); add_edge_2(l+2,r+3,inf,v);
    }
    for(int i=1;i<=n;++i) add_edge_2(i+3,i+2,inf,0);
    printf("%lld",mcmf());
}

      

BZOJ1061-[Noi2008]誌願者招募