[cogs396] [网络流24题#4] 魔术球 [网络流,最大流,最小路径覆盖]
本题枚举每多一个球需要多少个柱子,可以边加边边计算,每次只需要判断$i-Dinic()$即可;特别注意边界。
#include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <queue> using namespace std; template<const int _n> struct Edge { struct Edge_base { int to,w,next; }e[_n]; int cnt,p[_n]; Edge() { clear(); } void clear() { cnt=1,memset(p,0,sizeof(p)); } int start(const int x) { return p[x]; } Edge_base& operator[](const int x) { return e[x]; } void insert(const int x,const int y,const int z) { e[++cnt].to=y; e[cnt].next=p[x]; e[cnt].w=z; p[x]=cnt; return ; } }; int n,level[4100],cur[4100],SSS,TTT,Ans; Edge<210000> e; bool Bfs(const int S) { int i,t; queue<int> Q; memset(level,0,sizeof(level)); level[S]=1; Q.push(S); while(!Q.empty()) { t=Q.front(),Q.pop(); for(i=e.start(t);i;i=e[i].next) { if(!level[e[i].to] && e[i].w) { level[e[i].to]=level[t]+1; Q.push(e[i].to); } } } return level[TTT]; } int Dfs(const int S,const int bk) { if(S==TTT)return bk; int rest=bk; for(int &i=cur[S];i;i=e[i].next) { if(level[e[i].to]==level[S]+1 && e[i].w) { int flow=Dfs(e[i].to,min(rest,e[i].w)); e[i].w-=flow; e[i^1].w+=flow; if((rest-=flow)<=0)break; } } if(rest==bk)level[S]=0; return bk-rest; } int Dinic() { while(Bfs(SSS)) { memcpy(cur,e.p,sizeof(cur)); Ans+=Dfs(SSS,0x3f3f3f3f); } return Ans; } int Build() { SSS=4001,TTT=SSS+1; int j; e.insert(SSS,1,1); e.insert(1,SSS,0); e.insert(2001,TTT,1); e.insert(TTT,2001,0); for(j=1;j-Dinic()<=n;) { j++; e.insert(SSS,j,1),e.insert(j,SSS,0); e.insert(j+2000,TTT,1),e.insert(TTT,j+2000,0); for(int i=1;i<j;++i) { int t=i+j; for(int k=1;k*k<=t;++k) if(t==k*k) { e.insert(i,j+2000,1); e.insert(j+2000,i,0); break; } } } return j; } int main() { freopen("balla.in","r",stdin); freopen("balla.out","w",stdout); int mid; scanf("%d",&n); mid=Build(); printf("%d\n",mid-1); return 0; }