1. 程式人生 > 實用技巧 >[費用流] HDU 6767 New Equipments

[費用流] HDU 6767 New Equipments

[費用流] HDU 6767 New Equipments

題目大意

懶得翻譯了,自己看

Hall 定理

設二部圖\(G=\langle V_1,V_2,E\rangle\)中,\(|V_1|\leq|V_2|\). \(G\) 中存在從 \(V_1\)\(V_2\) 的完備匹配當且僅當 \(V_1\) 中任意 \(k\) 個頂點至少與 \(V_2\) 中的 \(k\) 個頂點相鄰 \((k=1,2,\dots,|V_1|)\).

設二部圖 \(G=\langle V_1,V_2,E\rangle\) 中, 如果存在 \(t\geq 1\), 使得 \(V_1\) 中每個頂點至少關聯 \(t\)

條邊, 而 \(V_2\) 中每個頂點至多關聯 \(t\) 條邊,則 \(G\) 中存在 \(V_1\)\(V_2\) 的完備匹配。

題解

對於這道題,因為最多隻有50個開口向上的二次函式,我們可以在每個二次函式的對稱軸附近選取50個點,然後每個代表二次函式的點向每個代表 \(x\) 值的點連邊,邊權是二次函式在 \(x\) 處對應的函式值。由 Hall 定理可知,該二分圖一定存在完備匹配。建完圖後直接跑一遍費用流求出最小費用即可。

這道題比賽時想到了正解沒寫出來,太可惜了。一是沒有開long long,二是他要求匹配數分別為 \(1\sim n\) 的最小代價。注意到每次找到的增廣路增廣的流量至多為1,只要每增廣一次,記錄一下答案即可,而不用跑 \(n\)

次費用流。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;

#define RG register int
#define LL long long

const LL INF=1LL<<60;
struct edge{int next,to,c;LL w;};
edge G[1000010];
int head[3010];
LL Dis[3010];
int Pre[3010];
int incf[3010];
bool inQ[3010];
LL a[60],b[60],c[60];
int N,M,S,T,cnt=2,MaxFlow=0;
LL MinCost=0;

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

inline void add_edge(int u,int v,int c,LL w){
    G[cnt].c=c;
    G[cnt].w=w;
    G[cnt].to=v;
    G[cnt].next=head[u];
    head[u]=cnt++;
    return;
}

bool SPFA(){
    queue<int> Q;
    memset(Dis,0x3f,sizeof(Dis));    
    memset(inQ,0,sizeof(inQ));
    Q.push(S);Dis[S]=0;incf[S]=1<<29;inQ[S]=true;
    while(!Q.empty()){
        int now=Q.front();Q.pop();inQ[now]=false;
        for(int i=head[now];i;i=G[i].next){
            if(!G[i].c) continue;
            if(Dis[G[i].to]>Dis[now]+G[i].w){
                Dis[G[i].to]=Dis[now]+G[i].w;
                incf[G[i].to]=min(incf[now],G[i].c);
                Pre[G[i].to]=i;
                if(!inQ[G[i].to]){
                    Q.push(G[i].to);
                    inQ[G[i].to]=true;
                }
            }
        }
    }
    if(Dis[T]>INF) return false;
    return true;
}

inline void Update(){
    int u=T;
    while(u!=S){
        int i=Pre[u];
        G[i].c-=incf[T];
        G[i^1].c+=incf[T];
        u=G[i^1].to;
    }
    MaxFlow+=incf[T];
    MinCost+=Dis[T]*incf[T];
    return;
}

vector<int> Range;
int Ans[100];
int Test;

int main(){
    Read(Test);
    while(Test--){
        int n,m;
        Read(n);Read(m);
        for(RG i=1;i<=n;++i){
            Read(a[i]);
            Read(b[i]);
            Read(c[i]);
        }
        Range.clear();
        for(RG i=1;i<=n;++i){
            int center=-b[i]/(a[i]<<1);
            int Len=n/2+2;
            if(center-Len+1<1){
                for(RG j=1;j<=min(m,Len*2);++j)
                    Range.push_back(j);
            }else if(center+Len-1>m){
                for(RG j=max(1,m-Len*2);j<=m;++j)
                    Range.push_back(j);
            }else{
                for(RG j=max(1,center-Len+1);j<=min(m,center+Len-1);++j)
                    Range.push_back(j);
            }
        }
        sort(Range.begin(),Range.end());
        Range.erase(unique(Range.begin(),Range.end()),Range.end());
        int Num=Range.size();
        cnt=2;
        memset(head,0,sizeof(head));
        MaxFlow=MinCost=0;
        for(RG i=0;i<Num;++i){
            LL x=Range[i];
            for(RG j=1;j<=n;++j){
                LL w=a[j]*x*x+b[j]*x+c[j];
                add_edge(Num+j,i+1,1,w);
                add_edge(i+1,Num+j,0,-w);
            }
        }
        N=Num+n+3;
        int SS=Num+n+1;
        T=Num+n+2;S=Num+n+3;
        for(RG i=1;i<=n;++i){
            add_edge(SS,Num+i,1,0);
            add_edge(Num+i,SS,0,0);
        }
        for(RG i=1;i<=Num;++i){
            add_edge(i,T,1,0);
            add_edge(T,i,0,0);
        }
        add_edge(SS,S,0,0);
        add_edge(S,SS,n,0);
        
        for(RG k=1;k<=n;++k){
            if(SPFA()) Update();
            printf("%lld",MinCost);
            if(k<n) printf(" ");
        }
        printf("\n");
    }
    return 0;
}