BZOJ1061-[Noi2008]誌願者招募
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]誌願者招募