1. 程式人生 > 其它 >GMOJ 2429. 【NOI2008】志願者招募 題解

GMOJ 2429. 【NOI2008】志願者招募 題解

思路

oh,我可愛的一眼網路流題。

話說為什麼一天一道網路流

這個做法好像比較清奇。

非常清奇你根本找不到一樣的

之前口胡過最長 k 可重區間集 ,所以很快就有思路了。

首先對於區間 \([l,r]\) ,我們把 \(l\)\(r+1\) 連一條無限流量輸入費用的邊。

然後考慮因為是至少覆蓋所以志願者(流量)不能在區間不覆蓋時移動,但是顯然區間會重。

於是我們對於每一個點 \(i\)\(i-1\) 連一條無限流量無費用的邊,讓流量可以在兩個重疊區間中移動。

再考慮每天人數不一樣,這個比較顯然,需要就從源點補,多了就退給匯點。

顯然匯點直接為 \(n+1\) 這樣減少一些處理。

如樣例建圖為:

graph TD 0((S))--1/0-->2((2)) 0((S))--2/0-->1((1)) 0((S))--1/0-->3((3)) 3((3))--INF/2-->4((T)) 2((2))--INF/5-->4((T)) subgraph 1((1))--INF/2-->3((3)) 2((2))--INF/0-->1((1)) 3((3))--INF/0-->2((2)) end

CODE

因為資料比較大寫了一個 SPFA-PMF 核心的 Primal-Dual 原始對偶演算法優化 zkw 費用流。

事實證明並不需要。

#include<ctime>
#include<cstdio>
#include<cstring>
#include<iostream>
#define reg register
#define uni unsigned
#define ll long long
using namespace std;
const int N=1010,M=40010,FSIZE=1<<20,INF=0x7f7f7f7f,W=4;
int n,m,d[N<<3],a[N],last,map[N];
struct edge{
    int l,t;ll v,c;
}e[M];
ll ht[N],w[N],mincost;
bool vis[N];
char BuF[FSIZE],*InF=BuF;
template<typename T>void read(T &x){
    for(;47>*InF||*InF>58;++InF);
    for(x=0;47<*InF&&*InF<58;x=x*10+(*InF++^48));
}
void add(int x,int y,int z,ll c){
    e[last]=(edge){a[x],y,z,c};
    a[x]=last++;
}
void Add(int x,int y,int z,ll c){
    add(x,y,z,c);
    add(y,x,0,-c);
}
void spfa(){
    memset(ht,127,sizeof(ht));
    for(reg int h=0,t=ht[0]=0;h<=t;++h){
        reg int hd=d[h];
        for(reg int i=a[hd];~i;i=e[i].l){
            reg int to=e[i].t;ll p=ht[hd]+e[i].c;
            if(e[i].v&&ht[to]>p){
                ht[to]=p;
                if(!vis[to]) vis[d[++t]=to]=1;
            }
        }
        vis[hd]=0;
    }
}
uni seed=time(0);
int rnd(const int &l,const int &r){
    seed^=seed<<17;seed^=seed>>13;seed^=seed>>5;
    return(seed%(r-l+1)+l);
}
void change(int &x,int &y){
    swap(map[x],map[y]);
    swap(x,y);
}
bool spfa_PMF(){
    memset(w,127,sizeof(w));
    for(reg int h=0,t=w[0]=0;h<=t;++h){
        reg int &hd=d[h];
        for(reg int i=0,*zh=d+h+1,*tl=d+t;i<W&&zh<tl;++i,++zh,--tl){
            if(w[hd]>w[*zh]) change(hd,*zh);
            if(w[hd]>w[*tl]) change(hd,*tl);
        }
        for(reg int i=a[hd];~i;i=e[i].l){
            reg int to=e[i].t;ll p=w[hd]+e[i].c+ht[hd]-ht[to];
            if(e[i].v&&w[to]>p){
                w[to]=p;
                if(!map[to]){
                    d[map[to]=++t]=to;
                    if(t>h+1&&w[d[t]]>w[d[t-1]]) change(d[t],d[t-1]);
                }else if(w[to]<w[d[t]]) change(d[map[to]],d[t]);
            }
        }
        map[hd]=vis[hd]=0;
    }
    return(w[n+1]<INF);
}
int dfs(int x,ll flow){
    if(x==n+1||!flow) return(flow);
    vis[x]=1;
    ll aflow,used=0;
    for(int i=a[x];~i;i=e[i].l){
        edge &g=e[i];
        if(!vis[g.t]&&g.v&&w[x]+g.c+ht[x]-ht[g.t]==w[g.t]&&(aflow=dfs(g.t,min(g.v,flow-used)))){
            g.v-=aflow;
            e[i^1].v+=aflow;
            mincost+=aflow*g.c;
            if((used+=aflow)==flow) break;
        }
    }
    return(used);
}
void PMF_SSP(){
    for(spfa();spfa_PMF();){
        dfs(0,INF);
        for(reg int i=0;i<=n+1;++i) if(w[i]<INF) ht[i]+=w[i];
    }
}
int main(){
    fread(BuF,1,FSIZE,stdin);
    memset(a,-1,sizeof(a));
    read(n);read(m);
    read(d[1]);
    Add(0,1,d[1],0);
    for(reg int i=2;i<=n;++i){
        read(d[i]);
        Add(i,i-1,INF,0);
        if(d[i]>d[i-1]) Add(0,i,d[i]-d[i-1],0);
        else Add(i,n+1,d[i-1]-d[i],0);
    }
    for(reg int x,y,z;m;--m){
        read(x);read(y);read(z);
        Add(x,y+1,INF,z);
    }
    PMF_SSP();
    printf("%lld",mincost);
    return(0);
}