[費用流] HDU 6767 New Equipments
阿新 • • 發佈:2020-07-24
[費用流] 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\)
題解
對於這道題,因為最多隻有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; }