[HZNOI #koishi] Magic

[HZNOI #514] Magic

題意

給定一個 \(n\) 個點 \(m\) 條邊的有向圖, 每一個點有兩個權值 \(a_i\)\(b_i\), 能夠以 \(b_i\) 的花費把第 \(i\) 個點的 \(a_i\) 變成 \(0\). 最後每一個點 \(i\) 產生的花費爲全部從 \(i\) 出發能經過一條有向邊直接到達的點 \(j\)\(a_j\)\(\max\). 最小化這個過程當中的總花費.c++

\(n\le 1000,m\le50000\)網絡

題解

一點都不套路的最小割.spa

果真我是不會網絡流的.rest

對於每一個點, 若是將它的鄰接點按照 \(a_j\) 降序排序的話, 不難發現必然要幹掉一個前綴的全部 \(a_j\) 才能讓這個點在最後統計的時候產生的花費變小. 可是屢次幹掉同一個點不能重複計算花費.code

那麼咱們一點都不天然地想到最小割. 先把全部點拆成兩個, 一個負責計算最終統計時的花費 (A類點), 一個負責計算被幹掉的時候產生的花費 (B類點). 被幹掉的時候產生的花費直接連一條流量爲 \(b_i\) 的邊到 \(t\) 就能夠了. 最終統計時的花費先從 \(s\) 連一條 \(\infty\) 邊到當前點, 而後按照 \(a_j\) 降序拉出一條鏈來, 鏈上的每一個點表明一條邊, 權值爲這條邊到達的點的 \(a_j\). 而後再從鏈上的每一個點連一條 \(\infty\) 邊到 \(j\) 對應的點. 這樣的話若是 \(s\verb|-|t\) 被割斷, 那麼對於每個 A 類點, 後面必然是割掉了某個 \(a_j\), 同時全部大於被割斷的 \(a_j\) 的邊鄰接的點必然都已經被割掉了 \(b_i\).blog

建圖Dinic就能夠了.排序

這個拉鍊而後最小割的套路依然沒有學會...果真我仍是太菜了QAQ...get

什麼你問我 \(n+m\) 個點Dinic怎麼跑過去的? 我怎麼知道?Dinic的運行速度大概都是靠信仰吧...it

戀戀世界第一!ast

參考代碼

#include <bits/stdc++.h>

const int MAXV=1e5+10;
const int MAXE=5e6+10;
const int INF=0x7FFFFFFF;

struct Edge{
    int from;
    int to;
    int flow;
    Edge* rev;
    Edge* next;
};
Edge E[MAXE];
Edge* head[MAXV];
Edge* cur[MAXV];
Edge* top=E;

int v;
int e;
int a[1010];
int b[1010];
int depth[MAXV];
std::vector<int> link[1010];

bool BFS(int,int);
int Dinic(int,int);
int DFS(int,int,int);
void Insert(int,int,int);

int main(){
    freopen("magic.in","r",stdin);
    freopen("magic.out","w",stdout);
    scanf("%d%d",&v,&e);
    for(int i=1;i<=v;i++)
        scanf("%d",a+i);
    for(int i=1;i<=v;i++)
        scanf("%d",b+i);
    for(int i=0;i<e;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        link[a].push_back(b);
    }
    for(int i=1;i<=v;i++)
        std::sort(link[i].begin(),link[i].end(),[](int a,int b){return ::a[a]>::a[b];});
    int s=0,t=1,cnt=v*2+1;
    for(int i=1;i<=v;i++){
        Insert(s,i+1,INF);
        Insert(i+v+1,t,b[i]);
        int last=i+1;
        for(size_t j=0;j<link[i].size();j++){
            ++cnt;
            Insert(cnt,v+link[i][j]+1,INF);
            Insert(last,cnt,a[link[i][j]]);
            last=cnt;
        }
    }
    printf("%d\n",Dinic(s,t));
    return 0;
}

int Dinic(int s,int t){
    int ans=0;
    while(BFS(s,t))
        ans+=DFS(s,INF,t);
    return ans;
}

bool BFS(int s,int t){
    memset(depth,0,sizeof(depth));
    std::queue<int> q;
    q.push(s);
    depth[s]=1;
    cur[s]=head[s];
    while(!q.empty()){
        s=q.front();
        q.pop();
        for(Edge* i=head[s];i!=NULL;i=i->next){
            if(i->flow>0&&depth[i->to]==0){
                depth[i->to]=depth[s]+1;
                cur[i->to]=head[i->to];
                if(i->to==t)
                    return true;
                q.push(i->to);
            }
        }
    }
    return false;
}

int DFS(int s,int flow,int t){
    if(s==t||flow<=0)
        return flow;
    int rest=flow;
    for(Edge*& i=cur[s];i!=NULL;i=i->next){
        if(i->flow>0&&depth[i->to]==depth[s]+1){
            int tmp=DFS(i->to,std::min(rest,i->flow),t);
            if(tmp<=0)
                depth[i->to]=0;
            rest-=tmp;
            i->flow-=tmp;
            i->rev->flow+=tmp;
            if(rest<=0)
                break;
        }
    }
    return flow-rest;
}

inline void Insert(int from,int to,int flow){
    top->from=from;
    top->to=to;
    top->flow=flow;
    top->rev=top+1;
    top->next=head[from];
    head[from]=top++;
    
    top->from=to;
    top->to=from;
    top->flow=0;
    top->rev=top-1;
    top->next=head[to];
    head[to]=top++;
}

相關文章
相關標籤/搜索