【TopCoder SRM590】Fox And City

相关链接

题目传送门:http://oi.cyo.ng/wp-content/uploads/2016/12/FoxAndCity.html
中文题面:http://paste.ubuntu.com/23693047/

解题报告

这题第一眼看到后觉得绝逼是一个DP
然而是网络流 QwQ

考虑原图,实际上是给定了每个点的距离上限和一些形如 \({dis_i}<={dis_j}+1\) 的关系
再考虑建边,就是更改一个点的 \(dis\),注意可能会引起一系列点的 \(dis\)的变化
这样的话,套用BZOJ 3144的模型就可以啦!
于是跑一个最小割就好

Code

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 30000;
const int M = 500000;
const int INF = 1e9;

int n,E,S,T,head[N],to[M],nxt[M],flow[M],MX[N];

class Network_Flow{
    int cur[N],dis[N];
    queue<int> que;
    public:
        inline int MaxFlow() {
            int ret = 0;
            while (BFS()) {
                memcpy(cur, head, sizeof(head));
                ret += DFS(S, INF);
            }   
            return ret;
        }
    private:
        inline bool BFS() {
            memset(dis,60,sizeof(dis));
            que.push(S); dis[S] = 0;
                
            while (!que.empty()) {
                int w = que.front(); que.pop();
                for (int i=head[w];i;i=nxt[i]) {
                    if (dis[to[i]] > INF && flow[i]) {
                        dis[to[i]] = dis[w] + 1;
                        que.push(to[i]);
                    }
                }
            }
            return dis[T] < INF;
        }
        int DFS(int w, int f) {
            if (w == T) return f;
            else {
                int ret = 0;
                for (int tmp,&i=cur[w];i;i=nxt[i]) {
                    if (dis[to[i]] == dis[w] + 1 && flow[i]) {
                        tmp = DFS(to[i], min(f, flow[i])); 
                        flow[i] -= tmp; flow[i^1] += tmp;
                        f -= tmp; ret += tmp;
                        if (!f) break;
                    }
                }
                return ret;
            }
        }
}Dinic;

class FoxAndCity {
	int dis[100][100];
    public:
    	int minimalCost(vector<string> linked, vector<int> want) {
    	    init();
    	    n = want.size();
    	    for (int i=0;i<n;i++) {
				for (int j=0;j<n;j++) {
					if (linked[i][j] == 'Y') dis[i+1][j+1] = 1;
					else dis[i+1][j+1] =  INF;
				}
			}
			Floyd();
			for (int i=2;i<=n;i++) 
				MX[i] = dis[1][i];
			init(); S = 0; T = N - 1;
			for (int i=2;i<=n;i++) {
				Add_Edge(S, id(i, 1));
				Add_Edge(id(i, MX[i]+1), T);
				for (int j=1;j<=MX[i];j++) {
					Add_Edge(id(i,j), id(i,j+1), sqr(want[i-1]-j));
					for (int k=2;k<=n;k++) {
						if (i != k && dis[i][k] == 1 && j > 1) {
							Add_Edge(id(i, j), id(k, j-1));
						}
					}
				}	
			}
			return Dinic.MaxFlow();
   		}
   	private:
   		inline void init() {
			E = 1;
			memset(head,0,sizeof(head));   
		}
		inline int id(int x, int y) {
			return (x-1) * (n+1) + y;
		}
		inline int sqr(int w) {
			return w * w;
		}
   		inline void Add_Edge(int u, int v, int f = INF) {
			to[++E] = v; nxt[E] = head[u]; head[u] = E; flow[E] = f;   	
			to[++E] = u; nxt[E] = head[v]; head[v] = E; flow[E] = 0;
		}
		inline void Floyd() {
			for (int k=1;k<=n;k++) {
				for (int i=1;i<=n;i++) {
					for (int j=1;j<=n;j++) {
						dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
					}
				}
			}
		}
};

【BZOJ 3876】[AHOI2014] 支线剧情

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3876

解题报告

就是裸的上下界费用流
但是合并一下边,快了30倍是什么鬼啊

Code

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 300 + 9;
const int M = 20000 + 100 << 1;
const int INF = 1e9;

int n,m,vout,T,S,V,cnt[N],head[N],to[M],nxt[M],cost[M],flow[M];

inline int read() {
	char c=getchar(); int f=1,ret=0;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret * f;
}

inline void Add_Edge(int u, int v, int f, int c) {
	static int E = 1;
	to[++E] = v; nxt[E] = head[u]; head[u] = E; flow[E] = f; cost[E] = c;
	to[++E] = u; nxt[E] = head[v]; head[v] = E; flow[E] = 0; cost[E] = -c; 
}

class Minimum_Cost_Flow{
	int dis[N],sur[N],inq[N];
	queue<int> que;
    public:
    	inline void MaxFlow(bool SPJ=0) {
			for (int f=INF,w;SPFA();f=INF) {
				if (SPJ && dis[T] >= 0) return;
				for (w=T;w!=S;w=to[sur[w]^1]) f = min(f, flow[sur[w]]);
				for (w=T;w!=S;w=to[sur[w]^1]) flow[sur[w]] -= f, flow[sur[w]^1] += f;
				vout += dis[T] * f;
			}
		}
	private:
		bool SPFA() {
			memset(dis,60,sizeof(dis));
			que.push(S); dis[S] = 0;
			
			while (!que.empty()) {
				int w = que.front(); que.pop(); inq[w] = 0;
				for (int i=head[w];i;i=nxt[i]) {
					if (dis[to[i]] > dis[w] + cost[i] && flow[i]) {
						dis[to[i]] = dis[w] + cost[i];
						sur[to[i]] = i;
						if (!inq[to[i]]) inq[to[i]] = 1, que.push(to[i]);
					}
				}
			}
			return dis[T] < INF;
		}
}MCMF;

int main() {
	n = read();
	S = 0; T = N - 1; V = N - 2;
	for  (int i=1,k;i<=n;i++) {
		k = read();
		for (int j=1,p,c;j<=k;j++) {
			p = read(); c = read();
			cnt[i]--; cnt[p]++;
			vout += c;
			Add_Edge(i, p, INF, c);
		}
		Add_Edge(i, V, INF, 0);
	}
	Add_Edge(V, 1, INF, 0);
	for (int i=1;i<=n;i++) {
		if (cnt[i] > 0) Add_Edge(S, i, cnt[i], 0);
		else Add_Edge(i, T, -cnt[i], 0);
	}
	MCMF.MaxFlow();
	for (int i=head[S];i;i=nxt[i]) 
		if (flow[i]) return 1;
	S = V; T = 1;
	MCMF.MaxFlow(1);
	printf("%d\n",vout);
	return 0;
}

【UOJ 77】A+B Problem

相关链接

题目传送门:http://uoj.ac/problem/77
神犇题解:http://www.cnblogs.com/geng4512/p/5296863.html

解题报告

我们发现如果忽略\(1<j<i\)这个限制,再假设\({l_i} = {r_i}\)
这样的话,直接上最小割就好

现在考虑\({l_i} < {r_i}\)
这样的话,用线段树优化建图就可以啦!

再考虑\(1<j<i\)这个限制
这样的话,用函数式线段树就可以啦!

感觉是VFK强行套数据结构啊!
另外还有BZOJ 3218可以双倍经验!
话说BZOJ上那些\(200ms+\)的神犇都是用的什么算法啊?
怎么这么快啊!

Code

#include<bits/stdc++.h>
using namespace std;

const int N = 200000+9;
const int M = 2000000;
const int INF = 1000000000;

int n,m,S,T,vout,head[N],nxt[M],to[M],flow[M];
int tot,_hash[N],A[N],W[N],B[N],LL[N],RR[N],P[N];

inline int read() {
	char c=getchar(); int f=1,ret=0;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret * f;
}

inline void Add_Edge(int u, int v, int f = INF, int t = 0) {
	static int E = 1; vout += f * t;
	to[++E] = v; nxt[E] = head[u]; head[u] = E; flow[E] = f;
	to[++E] = u; nxt[E] = head[v]; head[v] = E; flow[E] = 0;
}

class Network_Flow{
    int cur[N],dis[N];
    queue<int> que;
    public:
        inline int MaxFlow() {
            int ret = 0;
            while (BFS()) {
                memcpy(cur, head, sizeof(head));
                ret += DFS(S, INF);
            }   
            return ret;
        }
    private:
        inline bool BFS() {
            memset(dis,60,sizeof(dis));
            que.push(S); dis[S] = 0;
              
            while (!que.empty()) {
                int w = que.front(); que.pop();
                for (int i=head[w];i;i=nxt[i]) {
                    if (dis[to[i]] > INF && flow[i]) {
                        dis[to[i]] = dis[w] + 1;
                        que.push(to[i]);
                    }
                }
            }
            return dis[T] < INF;
        }
        int DFS(int w, int f) {
            if (w == T) return f;
            else {
                int ret = 0;
                for (int tmp,&i=cur[w];i;i=nxt[i]) {
                    if (dis[to[i]] == dis[w] + 1 && flow[i]) {
                        tmp = DFS(to[i], min(f, flow[i])); 
                        flow[i] -= tmp; flow[i^1] += tmp;
                        f -= tmp; ret += tmp;
                        if (!f) break;
                    }
                }
                return ret;
            }
        }
}Dinic;

namespace Persistent_Segment_Tree{
	#define PST Persistent_Segment_Tree
	int ch[N][2],root[N],cnt,pur,sur,L,R;
	
	void insert(int p, int &w, int l, int r, int f = 0) {
		if (w = ++cnt, p) {
			ch[w][0] = ch[p][0];
			ch[w][1] = ch[p][1];
			Add_Edge(p, w);
		} 
		if (f) Add_Edge(w, f);
		if (l < r) {
			int mid = l + r + 1 >> 1;
			if (pur < mid) insert(ch[p][0], ch[w][0], l, mid-1, w);
			else insert(ch[p][1], ch[w][1], mid, r, w);
		} else Add_Edge(sur, w);
	}
	
	inline void insert(int p, int v) {
		pur = v; sur = p;
		insert(root[p-1], root[p], 1, tot);
	}
	
	void modify(int w, int l, int r) {
		if (!w) return;
		else if (L <= l && r <= R) Add_Edge(w, pur);
		else {
			int mid = l + r + 1 >> 1;
			if (L < mid) modify(ch[w][0], l, mid-1);
			if (mid <= R) modify(ch[w][1], mid, r);
		}
	}
	
	inline void modify(int p, int node, int l, int r) {
		pur = node; L = l; R = r;
		modify(root[p], 1, tot);
	}
};

int main() {
	n = read(); PST::cnt = n << 1;
	S = 0; T = N - 1;
	for (int i=1;i<=n;i++) {
		_hash[++tot] = A[i] = read(); 
		B[i] = read();
		W[i] = read();
		_hash[++tot] = LL[i] = read();
		_hash[++tot] = RR[i] = read();
		P[i] = read();
	}
	sort(_hash+1, _hash+1+tot);
	tot = unique(_hash+1, _hash+1+tot) - _hash - 1;
	for (int i=1;i<=n;i++) {
		A[i] = lower_bound(_hash+1, _hash+1+tot, A[i]) - _hash;
		LL[i] = lower_bound(_hash+1, _hash+1+tot, LL[i]) - _hash;
		RR[i] = lower_bound(_hash+1, _hash+1+tot, RR[i]) - _hash;
	}
	for (int i=1,l,r;i<=n;i++) {
		PST::insert(i, A[i]);
		Add_Edge(i, T, B[i], 1);
		Add_Edge(S, i, W[i], 1);
		PST::modify(i-1, i+n, LL[i], RR[i]);
		Add_Edge(i+n, i, P[i]);
	}
	printf("%d\n",vout-Dinic.MaxFlow());
	return 0;
}

【BZOJ 1497】[NOI2006] 最大获利

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1497

解题报告

这题先考虑一种特别简单的建图方式:

考虑使用最小割来解决这个问题
将基站放在一边,客户放在另外一边
这样的话,我们不妨规定最后在S集的表示不选择,T集选择
考虑每一条增光路,必然要割掉客户到T的路径,或者基站到S的路径

酱紫的话,岂不是刚好满足条件?

当然使用以下这种经典的建图方式可以更优地建图:

Code

第一种建图方式:

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 55000+9;
const int M = N + 100000 << 1;
const int INF = 1e9;

int n,m,S,T,vout,head[N],nxt[M],to[M],flow[M];

inline int read() {
	char c=getchar(); int f=1,ret=0;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret * f;
}

inline void Add_Edge(int u, int v, int f = INF) {
	static int E = 1;
	to[++E] = v; nxt[E] = head[u]; head[u] = E; flow[E] = f;
	to[++E] = u; nxt[E] = head[v]; head[v] = E; flow[E] = 0;
}

class Network_Flow{
    int cur[N],dis[N];
    queue<int> que;
    public:
        inline int MaxFlow() {
            int ret = 0;
            while (BFS()) {
                memcpy(cur, head, sizeof(head));
                ret += DFS(S, INF);
            }   
            return ret;
        }
    private:
        inline bool BFS() {
            memset(dis,60,sizeof(dis));
            que.push(S); dis[S] = 0;
             
            while (!que.empty()) {
                int w = que.front(); que.pop();
                for (int i=head[w];i;i=nxt[i]) {
                    if (dis[to[i]] > INF && flow[i]) {
                        dis[to[i]] = dis[w] + 1;
                        que.push(to[i]);
                    }
                }
            }
            return dis[T] < INF;
        }
        int DFS(int w, int f) {
            if (w == T) return f;
            else {
                int ret = 0;
                for (int tmp,&i=cur[w];i;i=nxt[i]) {
                    if (dis[to[i]] == dis[w] + 1 && flow[i]) {
                        tmp = DFS(to[i], min(f, flow[i])); 
                        flow[i] -= tmp; flow[i^1] += tmp;
                        f -= tmp; ret += tmp;
                        if (!f) break;
                    }
                }
                return ret;
            }
        }
}Dinic;

int main() {
	n = read(); m = read();
	S = 0; T = N - 1;
	for (int i=1;i<=n;i++)
		Add_Edge(S, i, read());
	for (int i=1,t;i<=m;i++) {
		Add_Edge(read(), i+n);
		Add_Edge(read(), i+n);
		vout += (t = read());
		Add_Edge(i+n, T, t);
	}
	printf("%d\n",vout-Dinic.MaxFlow());
	return 0;
}

第二种建图方式:

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 5000 + 9;
const int M = 100000 << 1;
const int INF = 1e9;

int n,m,S,T,vout,tmp[N],head[N],nxt[M],to[M],flow[M];

inline int read() {
	char c=getchar(); int f=1,ret=0;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret * f;
}

inline void Add_Edge(int u, int v, int f = INF, int t = 0) {
	static int E = 1;
	to[++E] = v; nxt[E] = head[u]; head[u] = E; flow[E] = f;
	to[++E] = u; nxt[E] = head[v]; head[v] = E; flow[E] = f * t;
}

class Network_Flow{
    int cur[N],dis[N];
    queue<int> que;
    public:
        inline int MaxFlow() {
            int ret = 0;
            while (BFS()) {
                memcpy(cur, head, sizeof(head));
                ret += DFS(S, INF);
            }   
            return ret;
        }
    private:
        inline bool BFS() {
            memset(dis,60,sizeof(dis));
            que.push(S); dis[S] = 0;
             
            while (!que.empty()) {
                int w = que.front(); que.pop();
                for (int i=head[w];i;i=nxt[i]) {
                    if (dis[to[i]] > INF && flow[i]) {
                        dis[to[i]] = dis[w] + 1;
                        que.push(to[i]);
                    }
                }
            }
            return dis[T] < INF;
        }
        int DFS(int w, int f) {
            if (w == T) return f;
            else {
                int ret = 0;
                for (int tmp,&i=cur[w];i;i=nxt[i]) {
                    if (dis[to[i]] == dis[w] + 1 && flow[i]) {
                        tmp = DFS(to[i], min(f, flow[i])); 
                        flow[i] -= tmp; flow[i^1] += tmp;
                        f -= tmp; ret += tmp;
                        if (!f) break;
                    }
                }
                return ret;
            }
        }
}Dinic;

int main() {
	n = read(); m = read();
	S = 0; T = N - 1;
	for (int i=1;i<=n;i++)
		Add_Edge(i, T, read() << 1);
	for (int i=1,t,x,y;i<=m;i++) {
		x = read(); y = read();
		vout += (t = read()) * 2;
		tmp[x] += t; tmp[y] += t;
		Add_Edge(x, y, t, 1);
	}
	for (int i=1;i<=n;i++) 
		Add_Edge(S, i, tmp[i]);
	printf("%d\n",vout-Dinic.MaxFlow()>>1);
	return 0;
}

【算法笔记】上下界网络流问题

无源汇可行流

  1. 问题分析:
    因为只比传统网络流多了下界,所以考虑单独考虑下界的流量

  2. 解决方案:
    于是原来的边拆为上界为min的边并强行满流,和上界为max - min的边。
    但这样可能会出现流量不平衡的状况,及一个点流入的流量不等于流出量。
    于是单独增加源汇点,构造一个完全等价的网络流问题

    至此我们已经将问题转化为传统网络流问题,直接求解即可。
    如何判断是否有可行流的根据也显而易见了:\( Max\_Flow = = \sum {Min\_Flo{w_i}}\)

有源汇可行流

  1. 问题分析:
    有源汇意味着有一对点的流量不守恒,就是这一对点使其于无源汇可行流问题有了差别
    于是我们考虑去掉这个不同点,将其转化为无源汇可行流

  2. 解决方案
    我们注意到,将源汇点连上一条容量INF的边之后,所有的点流量都守恒了
    换一句话来说我们将其转化为了无源汇可行流问题,使用上文所述方法求解即可

有源汇最大流/最小流

  1. 通解通法:
    观察可行流的解决方案,不难发现我们控制我们人为增加的那条边的容量即可控制最大流的容量
    所以我们可以二分最大流/最小流,之后进行可行流判断,再之后根据结果进行调整即可

  2. 最小流的高效算法:
    先不连t-s的那条容量为INF的边,先跑一次附加源汇的最大流
    连t-s的那条边,在残量网络上继续跑附加源汇最大流
    此时t-s那条边的流量是最小的,使用上文所述方法判断其是否为可行流即可

  3. 最大流的高效算法
    连t-s的那条容量为INF的边,跑一次附加源汇最大流
    连t-s的那条边,在残量网络上继续跑s-t的最大流
    此时t-s那条边的流量是最小的,使用上文所述方法判断其是否为可行流即可

【BZOJ 3345】[Pku2914] Minimum Cut

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3345

这题就是一个全局最小割,其中分治最小割的做法肯定是正确的
于是水之,1.4s卡过

全局最小割专门的算法还是很优越的样子
无论是时间复杂度或是编程复杂度
但我这个蒟蒻学不会QAQ
弃坑

#include<bits/stdc++.h>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;
  
const int N = 850+9;
const int M = 8500*2+9;
const int INF = 100000000;
  
int head[N],nxt[M],to[M],flow[M],cur[N];
int n,m,vout=INF,cnt,id[N],dis[N],pur,T=1;
queue<int> que;
  
inline void Add_Edge(int u, int v, int w) {
    to[++T] = v; nxt[T] = head[u]; head[u] = T; flow[T] = w;
    to[++T] = u; nxt[T] = head[v]; head[v] = T; flow[T] = w;
}
  
inline int read(){
    char c=getchar(); int ret=0,f=1;
    while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
    while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
    return ret*f;
}
  
inline bool BFS(int s, int t) {
    memset(dis,-1,sizeof(dis));
    que.push(s); dis[s] = 0;
      
    while (!que.empty()) {
        int w = que.front(); que.pop();
        for (int i=head[w];i;i=nxt[i]) if (flow[i] && !~dis[to[i]]) 
            dis[to[i]] = dis[w] + 1, que.push(to[i]);
    }
    return ~dis[t];
}
  
int DFS(int w, int f) {
    if (w == pur) return f;
    else {
        int ret = 0;
        for (int &i=cur[w];i;i=nxt[i]) if (flow[i] && dis[to[i]] == dis[w] + 1) {
            int tmp = DFS(to[i], min(f, flow[i]));
            flow[i] -= tmp; flow[i^1] += tmp;
            ret += tmp; f -= tmp; if (!f) return ret;
        }
        return ret;
    }   
}
  
inline int Dinic(int s, int t){
    int ret = 0; pur = t;
    while (BFS(s,t)) {
        memcpy(cur,head,sizeof(head));
        ret += DFS(s,INF);
    } return ret;
}
  
void SIGN(int w) {
    dis[w] = 1;
    for (int i=head[w];i;i=nxt[i]) 
        if (!~dis[to[i]] && flow[i]) SIGN(to[i]);
}
  
void solve(int l , int r){
    if (l >= r) return ;
    static int tmp[N]; int L=l-1, R=r+1;
      
    for (int i=2;i<=T;i+=2) flow[i] = flow[i^1] = flow[i] + flow[i^1] >> 1;
    vout = min(vout, Dinic(id[l],id[r]));
      
    memset(dis,-1,sizeof(dis)); SIGN(id[l]); 
    for (int i=l;i<=r;i++) 
        if (~dis[id[i]]) tmp[++L] = id[i];
        else tmp[--R] = id[i];
    for (int i=l;i<=r;i++) id[i] = tmp[i];
    solve(l,L); solve(R,r);
}
  
int main(){
    n = read(); m = read();
    for (int i=1,u,v,w;i<=m;i++) u = read(), v = read(), w = read(), Add_Edge(u,v,w);
    for (int i=1;i<=n;i++) id[i] = i; solve(1,n); 
    printf("%d\n",vout);
    return 0;
}

【BZOJ 2095】[Poi2010] Bridges

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2095

这个题目啊!瞄一眼就知道是二分吧?
接下来就是给你一些有向边&无向边,让你判断是否存在欧拉回路

对于有向边,没有悬念,直接记录对于出度入度的贡献
只与有向边嘛,不妨先随便定一个向,然后考虑使用网络流进行调整
具体细节可以参见:http://blog.csdn.net/wzq_qwq/article/details/48651379

#include<bits/stdc++.h>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int N = 2000+9;
const int M = 10000+9;
const int INF = 1e9;

struct Edge{int u,v,w1,w2;}edge[M];
int n,m,MX,MN=INF,in[N],out[N],cur[M];
int S,T,dis[N],flow[M],head[N],nxt[M],to[M],TT; 
queue<int> que;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline void Add_Edge(int u, int v, int f) {
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f;
	to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0;
} 

inline bool BFS(){
	memset(dis,-1,sizeof(dis));
	dis[S] = 0; que.push(0);
	
	while (!que.empty()) {
		int w = que.front(); que.pop();
		for (int i=head[w];i;i=nxt[i]) if (flow[i] && !~dis[to[i]]) {
			dis[to[i]] = dis[w] + 1;
			que.push(to[i]);
		}
	}
	
	return ~dis[T];
}

int DFS(int w, int f) {
	if (w == T) {
		return f;
	} else {
		int ret = 0;
		for (int &i=cur[w],tmp;i && f;i=nxt[i]) { 
			if (dis[to[i]] == dis[w] + 1) {
				tmp = DFS(to[i], min(f, flow[i]));
				ret += tmp; f -= tmp;
				flow[i] -= tmp; flow[i^1] += tmp;
			}
		}
		return ret;
	}
}

inline int Dinic(){
	int ret = 0;
	while (BFS()) {
		memcpy(cur,head,sizeof(head));
		ret += DFS(S,INF);
	}
	return ret;
}

inline bool judge(int lim){
	memset(in,0,sizeof(in));
	memset(out,0,sizeof(out));
	memset(head,0,sizeof(head));
	S = 0; T = N - 1; TT = 1;
	int tot = 0;
	
	for (int i=1;i<=m;i++) {
		if (lim < edge[i].w1) {
			continue;
		} else if (lim < edge[i].w2) {
			in[edge[i].v]++;
			out[edge[i].u]++;
		} else {
			in[edge[i].v]++;
			out[edge[i].u]++;
			Add_Edge(edge[i].u, edge[i].v, 2);
		}
	}
	
	for (int i=1;i<=n;i++) {
		if (abs(in[i]-out[i]) & 1) {
			return false;
		} else if (in[i] < out[i]) {
			Add_Edge(S,i,out[i]-in[i]);	
			tot += out[i] - in[i];
		} else if (in[i] > out[i]) {
			Add_Edge(i,T,in[i]-out[i]);
		}
	}
	
	return Dinic() == tot;
}

int main(){
	n = read(); m = read();
	for (int i=1;i<=m;i++) {
		edge[i].u = read();
		edge[i].v = read();
		edge[i].w1 = read();	
		edge[i].w2 = read();
		if (edge[i].w1 > edge[i].w2) {
			swap(edge[i].w1, edge[i].w2);
			swap(edge[i].u, edge[i].v);
		}
		MX = max(MX, edge[i].w2);
		MN = min(MN, edge[i].w1);
	}
	
	int l = MN, r = MX, mid, ret = -1;
	while (l <= r) {
		mid = l + r >> 1;
		if (judge(mid)) ret = mid, r = mid - 1;
		else l = mid + 1;
	}
	if (~ret) printf("%d\n",ret);
	else puts("NIE");
	return 0;
}

【BZOJ 4514】[Sdoi2016] 数字配对

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4514

他都给你说了是匹配,那肯定是二分图匹配啦!
考虑可以整除,除完之后还是个质数,那么质因数的个数(不是种类)肯定是一奇一偶
于是建成二分图跑一跑MCMF()就行啦!
当然如果你比较刚烈,想跑带权带花树肯定也是可以的啦!

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int M = 200+9;
const int N = 100000+9;
const int MX = 100000; 
const int INF = 0x7fffffff;

int pri[N],tot,vis[N],n,A[M],B[M],C[M];
int lft[M],rit[M],tl,tr,sur[M],S,T,inq[M];
int head[M],nxt[N],to[N],flow[N];
LL cost[N],dis[M];
queue<int> que;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline void Get_Prime_Number(){
	for (int i=2;i<=MX;i++) {
		if (!vis[i]) pri[++tot] = i;
		for (int j=1;j<=tot&&i*pri[j]<=MX;j++) 
			if (i%pri[j]) vis[i*pri[j]] = 1;
			else {vis[i*pri[j]] = 1; break;}
	}
}

inline void Add_Edge(int u, int v, int f, LL c) {
	static int TT = 1;
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f; cost[TT] = c;
	to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0; cost[TT] = -c;
}

inline bool divide(int w) {int ret = 0; for (int i=1;i<=tot;i++) while (w%pri[i] == 0) w /= pri[i], ret++; return ret&1;}
inline bool is_prime(int w) {for (int i=1;i<=tot;i++) if (pri[i] >= w) break; else if (w%pri[i] == 0) return false; return true;}

inline bool SPFA(){
	memset(dis,127,sizeof(dis));
	que.push(S); inq[S] = 1; dis[S] = 0;
	
	while (!que.empty()) {
		int w = que.front(); que.pop(); inq[w] = 0;
		for (int i=head[w];i;i=nxt[i]) if (flow[i] && dis[to[i]] > dis[w] + cost[i]) {
			dis[to[i]] = dis[w] + cost[i]; sur[to[i]] = i;
			if (!inq[to[i]]) inq[to[i]] = 1, que.push(to[i]);
		}
	} return dis[T] < dis[M-2]; 
}

inline LL MCMF(){
	LL ret = 0, tot_cost = 0; bool tag=1;
	for (int w=T,f;SPFA()&&tag;w=T) {
		for (f=INF;w!=S;w=to[sur[w]^1]) f = min(f, flow[sur[w]]); 
		if (dis[T]*f+tot_cost > 0) ret += -tot_cost / dis[T], tag = 0;
		else {
			for (w=T;w!=S;w=to[sur[w]^1]) flow[sur[w]] -= f, flow[sur[w]^1] += f;
			ret += f; tot_cost += f*dis[T];
		}
	} return ret;
}

int main(){
	n = read(); Get_Prime_Number(); S = 0; T = M - 1;
	for (int i=1;i<=n;i++) A[i] = read();
	for (int i=1;i<=n;i++) B[i] = read();
	for (int i=1;i<=n;i++) C[i] = read();
	for (int i=1;i<=n;i++) 
		if (divide(A[i])) lft[++tl] = i, Add_Edge(S,i,B[i],0);
		else rit[++tr] = i, Add_Edge(i,T,B[i],0);
	for (int i=1;i<=tl;i++) for (int j=1;j<=tr;j++) {
		int w1 = A[lft[i]], w2 = A[rit[j]]; if (w1 > w2) swap(w1, w2); 
		if (w2 % w1 == 0 && is_prime(w2 / w1)) 
			Add_Edge(lft[i],rit[j],INF,-1LL*C[lft[i]]*C[rit[j]]);
	} printf("%lld\n",MCMF());
	return 0;
}

【BZOJ 1061】[Noi2008] 志愿者招募

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1061

这题好神……
作为单纯形的例题,用费用流给艹过去……
题解的话,我已经膜拜得五体投地了,让我们召唤BYvoid:https://www.byvoid.com/blog/noi-2008-employee/

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 1000+9;
const int M = 100000; 
const int INF = 1e9;

int head[N],nxt[M],to[M],flow[M],cost[M],dis[N];
int n,m,S,T,ned[N],inq[N],sur[N],FLOW[N];
queue<int> que;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}
inline void Add_Edge(int u, int v, int f, int c) {
    static int TT = 1;
    to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f; cost[TT] = c;
    to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0; cost[TT] = -c;
}
 
inline int SPFA() {
    memset(dis,127,sizeof(dis));
    memset(FLOW,0,sizeof(FLOW));
    que.push(S); dis[S] = 0; inq[S] = 1; FLOW[S] = INF;
       
    while (!que.empty()) {
        int w = que.front(); que.pop(); inq[w] = 0;
        for (int i=head[w];i;i=nxt[i]) if (flow[i] && dis[w] + cost[i] < dis[to[i]]) {
            dis[to[i]] = dis[w] + cost[i]; sur[to[i]] = i;
            FLOW[to[i]] = min(FLOW[w], flow[i]);
            if (!inq[to[i]]) que.push(to[i]), inq[to[i]] = 1;
        }
    } return FLOW[T]; 
}
   
inline int MCMF() {
    int ret = 0;
    for (int w=T,f;f=SPFA();w=T) for (int t=sur[w];w!=S;t=sur[w]) 
        flow[t] -= f, flow[t^1] += f, ret += cost[t]*f, w = to[t^1];
    return ret;
}

int main(){
	n = read(); m = read(); S = 0; T = N-1;
	for (int i=1;i<=n;i++) ned[i] = read();
	for (int i=n+1;i;i--) ned[i] = ned[i-1] - ned[i];
	for (int i=1;i<=n+1;i++) if (ned[i] > 0) Add_Edge(S,i,ned[i],0); else Add_Edge(i,T,-ned[i],0);
	for (int i=1;i<=n;i++) Add_Edge(i,i+1,INF,0);  
	for (int i=1,s,t,c;i<=m;i++) s = read(), t = read(), c = read(), Add_Edge(t+1,s,INF,c);
	printf("%d\n",MCMF());
	return 0;
}

【BZOJ 2879】[Noi2012] 美食节

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2879
数据生成器:http://paste.ubuntu.com/23196968/
官方数据:http://pan.baidu.com/s/1nv4ginB

这题就是修车的升级版
除了动态加点,别无其他神奇的玩意儿
接下来请欣赏《论S与T的区别》:
看起来厨师拆开的点连到T,与连到S没什么区别
然而…….
98765454

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int L = 100+9;
const int N = 1000+9;
const int M = N*100;
const int INF = 100000000; 


int n,m,head[N],nxt[M],to[M],cost[M],dis[N],flow[M];
int inq[N],cnt,cur[N],mat[L][L],id[N],vout,S,T,sur[N];
queue<int> que;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline void Add_Edge(int u, int v, int f, int c) {
	static int TT = 1;
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f; cost[TT] = c;
	to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0; cost[TT] = -c;
}

inline bool SPFA() {
	memset(dis,127,sizeof(dis));
	que.push(S); inq[S] = 1; dis[S] = 0;
	
	while (!que.empty()) {
		int w = que.front(); que.pop(); inq[w] = 0;
		for (int i=head[w];i;i=nxt[i]) if (flow[i] && dis[to[i]] > dis[w] + cost[i]) {
			dis[to[i]] = dis[w] + cost[i]; sur[to[i]] = i;
			if (!inq[to[i]]) que.push(to[i]), inq[to[i]] = 1;
		} 
	} return dis[T] < 1000000000;
}

inline void MCMF(){
	for (int nw;SPFA();) {
		for (int w=T;w!=S;w=to[sur[w]^1]) nw = id[w]?id[w]:nw, flow[sur[w]]--, flow[sur[w]^1]++;  
		id[++cnt] = nw; cur[nw]++; Add_Edge(S,cnt,1,0); vout += dis[T]; 
		for (int j=1;j<=n;j++) Add_Edge(cnt,j,1,mat[j][nw]*cur[nw]); 
 	}
}

int main(){
	n = read(); m = read(); S = 0; T = N - 1; cnt = n+m;
	for (int i=1,tmp;i<=n;i++) Add_Edge(i,T,tmp=read(),0);
	for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) mat[i][j] = read();
	for (int i=1;i<=m;i++) {
		id[i+n] = i; cur[i] = 1; Add_Edge(S,i+n,1,0);
		for (int j=1;j<=n;j++) Add_Edge(i+n,j,1,mat[j][i]);
	} MCMF(); printf("%d\n",vout);
	return 0;
}

【BZOJ 1458】士兵占领

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1458

大体上同BZOJ_1711
具体来说,假设全部填上,于是要删尽量多的点
然后行放左边,列放右边,点拆开放中间

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int L = 100+9;
const int N = 50000+9;
const int M = 1000000;
const int INF = 1000000000;

int head[N],nxt[M],to[M],dis[N],cur[N],flow[M];
int n,m,X[L],Y[L],cx[L],cy[L],k,S,T,mat[L][L],vout;
queue<int> que;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}
 
#define id(x,y) (x+(y-1)*n)
inline void Add_Edge(int u, int v, int f) {
    static int TT = 1;
    to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f;
    to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0;
}
  
inline bool BFS(){
    memset(dis,-1,sizeof(dis));
    dis[S] = 0; que.push(S);
    while (!que.empty()) {
        int w = que.front(); que.pop();
        for (int i=head[w];i;i=nxt[i]) if (flow[i] && !~dis[to[i]])
            dis[to[i]] = dis[w] + 1, que.push(to[i]);
    } return ~dis[T];
}
    
int DFS(int w, int f) {
    if (w == T) return f;
    else { int ret = 0;
        for (int &i=cur[w];i;i=nxt[i]) if (flow[i] && dis[to[i]] == dis[w] + 1) {
            int tmp = DFS(to[i], min(f, flow[i]));
            ret += tmp; f -= tmp; flow[i] -= tmp; flow[i^1] += tmp;
            if (!f) break;
        } return ret;
    }
}
    
inline int Dinic(){
    int ret = 0; while (BFS()) {
        memcpy(cur,head,sizeof(head));
        ret += DFS(S,INF);
    } return ret;
}

int main(){
	m = read(); n = read(); k = read(); S = 0, T = N-1; vout = n*m;
	for (int i=1;i<=m;i++) Y[i] = read(), cy[i] = n;
	for (int i=1;i<=n;i++) X[i] = read(), cx[i] = m;
	for (int i=1,x,y;i<=k;i++) x = read(), y = read(), mat[x][y] = 1, cx[x]--, cy[y]--, vout--;
	for (int i=1;i<=m;i++) if (cy[i] < Y[i]) puts("JIONG!"), exit(0);
	for (int i=1;i<=n;i++) if (cx[i] < X[i]) puts("JIONG!"), exit(0);
	
	for (int i=1;i<=n;i++) Add_Edge(S,i,cx[i]-X[i]);
	for (int i=1;i<=m;i++) Add_Edge(n+i,T,cy[i]-Y[i]);
	for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) if (!mat[i][j]) 
		Add_Edge(m+n+id(i,j),m+n+m+n+id(i,j),1),
		Add_Edge(i,m+n+id(i,j),1), Add_Edge(m+n+m+n+id(i,j),j+n,1);
	printf("%d\n",vout-Dinic());
	return 0;
}

【BZOJ 1711】[Usaco2007 Open] Dining吃饭

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1711

《网络流建模汇总》的例题
牛放中间,食物放左边,饮料放右边

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 400+9;
const int M = 1000000;
const int INF = 1000000000;

int S,T,f,d,n,head[N],nxt[M],to[M],flow[M],dis[N],cur[N];
queue<int> que; 

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline void Add_Edge(int u, int v, int f) {
    static int TT = 1;
    to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f;
    to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0;
}
 
inline bool BFS(){
    memset(dis,-1,sizeof(dis));
    dis[S] = 0; que.push(S);
    while (!que.empty()) {
        int w = que.front(); que.pop();
        for (int i=head[w];i;i=nxt[i]) if (flow[i] && !~dis[to[i]])
            dis[to[i]] = dis[w] + 1, que.push(to[i]);
    } return ~dis[T];
}
   
int DFS(int w, int f) {
    if (w == T) return f;
    else { int ret = 0;
        for (int &i=cur[w];i;i=nxt[i]) if (flow[i] && dis[to[i]] == dis[w] + 1) {
            int tmp = DFS(to[i], min(f, flow[i]));
            ret += tmp; f -= tmp; flow[i] -= tmp; flow[i^1] += tmp;
            if (!f) break;
        } return ret;
    }
}
   
inline int Dinic(){
    int ret = 0; while (BFS()) {
        memcpy(cur,head,sizeof(head));
        ret += DFS(S,INF);
    } return ret;
}

int main(){
	n = read(); f = read(); d = read(); S = 0; T = N-1;
	for (int i=1;i<=f;i++) Add_Edge(S,i,1);
	for (int i=1;i<=d;i++) Add_Edge(i+f+n,T,1);
	for (int i=1;i<=n;i++) Add_Edge(f+i,f+n+d+i,1);
	for (int i=1,t1,t2;i<=n;i++) {
		t1 = read(); t2 = read(); 
		for (int j=1;j<=t1;j++) Add_Edge(read(),i+f,1);
		for (int j=1;j<=t2;j++) Add_Edge(i+f+n+d,read()+f+n,1);
	} printf("%d\n",Dinic());
	return 0;
}

【BZOJ 1391】[Ceoi2008] order

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1391

喜闻乐见大水题

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 10000;
const int M = 4000000;
const int INF = 1000000000;

int n,m,head[N],nxt[M],to[M],flow[M],dis[N],cur[N],S,T,vout;
queue<int> que;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline void Add_Edge(int u, int v, int f) {
	static int TT = 1;
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f;
	to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0;
}

inline bool BFS(){
    memset(dis,-1,sizeof(dis));
    dis[S] = 0; que.push(S);
    while (!que.empty()) {
        int w = que.front(); que.pop();
        for (int i=head[w];i;i=nxt[i]) if (flow[i] && !~dis[to[i]])
            dis[to[i]] = dis[w] + 1, que.push(to[i]);
    } return ~dis[T];
}
  
int DFS(int w, int f) {
    if (w == T) return f;
    else { int ret = 0;
        for (int &i=cur[w];i;i=nxt[i]) if (flow[i] && dis[to[i]] == dis[w] + 1) {
            int tmp = DFS(to[i], min(f, flow[i]));
            ret += tmp; f -= tmp; flow[i] -= tmp; flow[i^1] += tmp;
            if (!f) break;
        } return ret;
    }
}
  
inline int Dinic(){
    int ret = 0; while (BFS()) {
        memcpy(cur,head,sizeof(head));
        ret += DFS(S,INF);
    } return ret;
}

int main(){
	n = read(); m = read(); S = 0; T = N-1;
	for (int i=1,t1,t2,t3;i<=n;i++) {
		t1 = read(); Add_Edge(S,i,t1); vout += t1;
		for (t2=read();t2;t2--) t3 = read(), Add_Edge(i,n+t3,read());
	}
	for (int i=1;i<=m;i++) Add_Edge(n+i,T,read());
	printf("%d\n",vout-Dinic());
	return 0;
}

【BZOJ 1070】[SCOI2007] 修车

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1070

这个题目,最开始想到的肯定是完全按照时间来拆点
这样想起来很直观,然而估计会wa?因为一个车是一个整体,不能中间暂停
于是只能考虑以车为单位来拆点。然后计算贡献。
这题真的是好妙啊!

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

int m,n,s,t,vout;// m means the worker     n means the car
int str[70][20];
struct edge{
	int from,to,cap,cost,flow;
	edge(int from,int to,int cap,int flow,int cost):from(from),to(to),cap(cap),cost(cost),flow(flow){}
	edge(){}
};
vector<edge> edges;
vector<int> node[700];//1-60 is the car  71-INF is the worker

void addedge(int from,int to,int cap,int flow,int cost){
	edges.push_back( edge(from,to,cap,0,cost) );
	edges.push_back( edge(to,from,0,0,-cost) );
	int asd=edges.size();
	node[from].push_back(asd-2);
	node[to].push_back(asd-1);
}

void init(){
	cin>>m>>n;
	for (int i=1,hc;i<=n;i++){//car
		for (int j=1;j<=m;j++){//worker
		    scanf("%d",&hc);//hc=-hc;
		    for (int k=1,hj;k<=n;k++){
                hj=n+(j-1)*n+k;
				addedge(i,hj,1,0,hc*k);		    	
		    }
		}
	}
	s=0;t=n+n*m+1;
	for (int i=1;i<=n;i++) addedge(0,i,1,0,0);
	for (int i=n+1;i<t;i++) addedge(i,t,1,0,0);
}

int d[1000],a[1000],p[1000],inq[1000];
bool bellman_ford(){
	queue<int> Q;Q.push(s);
	memset(inq,0,sizeof (inq));
	for (int i=1;i<=t;i++) d[i]=0x7ffffeee;
	a[s]=0x7ffffeee;d[s]=0;p[s]=0;inq[s]=1;
	
	while(!Q.empty()) {
		int now=Q.front();Q.pop();inq[now]=0;
		int len=node[now].size();
		for (int i=0;i<len;i++){
			edge& e=edges[node[now][i]];
			if (e.cap>e.flow && d[e.to]>d[now]+e.cost){
				d[e.to]=d[now]+e.cost;
				p[e.to]=node[now][i];
				a[e.to]=min(a[now],e.cap-e.flow);
				if (!inq[e.to]) inq[e.to]=1,Q.push(e.to);
			}
		}
	}
	if (d[t]==0x7ffffeee) return false;
	vout+=d[t]*a[t];
	for (int i=t;i!=s;i=edges[p[i]].from){
		edges[p[i]].flow+=a[t];
		edges[p[i]^1].flow-=a[t];
	}
	return true;
}

int main(){
	init();
	while (bellman_ford()) {}
	printf("%.2f",(double)vout/(double)n);
	return 0;
}

【BZOJ 3275】Number

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3275

这题还是挺有意思的!
首先,肯定一眼能看出来是最小割
然后就是建图了。
1)暴力方法,拆点:http://hzwer.com/2864.html ←这样搞虽然不优雅,但普适性更强?←这样做是错的,详见UPD
2)优雅的方法:写个程序试一试,发现在1000以内的冲突的a,b一定是一奇一偶,于是搞成二分图

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 3000+9;
const int M = 1000000;
const int INF = 1000000000;

int n,arr[N],head[N],nxt[M],to[M],flow[M],cur[N],dis[N],S,T,vout;
queue<int> que;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

#define sqr(x) ((x)*(x))
int GCD(int a, int b) {return b?GCD(b,a%b):a;}
inline void Add_Edge(int u, int v, int f) {
	static int TT = 1;
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f;
	to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0;
}

inline bool BFS(){
    memset(dis,-1,sizeof(dis));
    dis[S] = 0; que.push(S);
    while (!que.empty()) {
        int w = que.front(); que.pop();
        for (int i=head[w];i;i=nxt[i]) if (flow[i] && !~dis[to[i]])
            dis[to[i]] = dis[w] + 1, que.push(to[i]);
    } return ~dis[T];
}
 
int DFS(int w, int f) {
    if (w == T) return f;
    else { int ret = 0;
        for (int &i=cur[w];i;i=nxt[i]) if (flow[i] && dis[to[i]] == dis[w] + 1) {
            int tmp = DFS(to[i], min(f, flow[i]));
            ret += tmp; f -= tmp; flow[i] -= tmp; flow[i^1] += tmp;
            if (!f) break;
        } return ret;
    }
}
 
inline int Dinic(){
    int ret = 0; while (BFS()) {
        memcpy(cur,head,sizeof(head));
        ret += DFS(S,INF);
    } return ret;
}

int main(){
	n = read(); S = 0; T = N - 1;
	for (int i=1;i<=n;i++) arr[i] = read(), vout += arr[i];
	for (int i=1;i<=n;i++) if (arr[i]%2) Add_Edge(S,i,arr[i]); else Add_Edge(i,T,arr[i]);
	for (int i=1;i<=n;i++) if (arr[i]%2) for (int j=1,w=sqr(arr[i])+sqr(arr[j]);j<=n;j++,w=sqr(arr[i])+sqr(arr[j])) 
		if (GCD(arr[i],arr[j])==1 && sqr(int(sqrt(w)+1e-7))== w) Add_Edge(i,j,INF);
	printf("%d\n",vout-Dinic());
	return 0;
}

—– UPD 2016.9.17 —–
给一下证明:
首先同为偶数肯定不成立。
对于同为奇数的情况:
(2a-1)^2+(2b-1)^2=c^2
c^2=4(a(a-1)+b(b-1))+2
不难发现c^2中只有一个2,没有两个2
所以c不可能为整数
得证.

—– UPD 2016.9.18 —–
今天突然想到,如果hzwer那种建模可以在一般图中推广的话
那我们还要带花树干嘛?
所以这题黄学长没证二分图就艹过去了,这完全是运气好啊!
但再仔细想一想,如果是带花树的题,岂不是可以尝试这样去骗分?