BZOJ 2661: [BeiJing wc2012]连连看

Description

凡是考智商的题里面总会有这么一种消除游戏。不过现在面对的这关连连看可不是QQ游戏里那种考眼力的游戏。我们的规则是,给出一个闭区间[a,b]中的全部整数,如果其中某两个数x,y(设x>y)的平方差x2-y2是一个完全平方数z2,并且y与z互质,那么就可以将x和y连起来并且将它们一起消除,同时得到x+y点分数。那么过关的要求就是,消除的数对尽可能多的前提下,得到足够的分数。快动手动笔算一算吧。

Input

只有一行,两个整数,分别表示a,b。

Output

两个数,可以消去的对数,及在此基础上能得到的最大分数。

Sample Input

1 15

Sample Output

2 34

HINT

对于30%的数据,1<=a,b<=100

对于100%的数据,1<=a,b<=1000

Solution

在每一对可行的(不分大小)x,y之间建边,费用为x+y,跑最大费用流即可。

一开始我从大的点向小的点建边,发现WA了。如果我们只建立(大->小)的边的话,那么某些点会插入许多次!也就是说某些数可以用很多次。这样跑出来肯定WA了!

所以说我们要建立两条边,正着和反着都建边,跑出来才是正确答案。

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
const int INF = 1<<30;
typedef long long LL;
int getint() {
	int r = 0, k = 1; char c = getchar();
	for (; '0' > c || c > '9'; c = getchar()) if (c == '-') k = -1;
	for (; '0' <= c && c <= '9'; c = getchar()) r = r * 10 - '0' + c;
	return r * k;
}
struct edge_type {
	int to, next, r, c;
} edge[100005];
int S, T, N, M, dis[10005], a[10005], pre[10005], cnte = 1, h[10005];
bool vis[10005];
queue<int> Q;
void ins(int u, int v, int f, int c) {
	edge[++cnte].to = v;
	edge[cnte].next = h[u];
	h[u] = cnte;
	edge[cnte].r = f;
	edge[cnte].c = c;
	edge[++cnte].to = u;
	edge[cnte].next = h[v];
	h[v] = cnte;
	edge[cnte].r = 0;
	edge[cnte].c = -c;
}
bool SPFA(int &flow, int &cost) {
	for (int i = S; i <= T; ++i) dis[i] = INF, vis[i] = false;
	pre[S] = 0; dis[S] = 0; vis[S] = true; a[S] = INF;
	Q.push(S);
	int now;
	while (!Q.empty()) {
		now = Q.front(); Q.pop();
		vis[now] = false;
		for (int i = h[now]; i; i = edge[i].next) {
			if (!edge[i].r) continue;
			if (dis[edge[i].to] > dis[now] + edge[i].c) {
				dis[edge[i].to] = dis[now] + edge[i].c;
				a[edge[i].to] = min(a[now], edge[i].r);
				pre[edge[i].to] = i;
				if (!vis[edge[i].to]) {
					vis[edge[i].to] = true;
					Q.push(edge[i].to);
				}
			}
		}
	}
	if (dis[T] == INF) return false;
	flow += a[T];
	cost += a[T] * dis[T];
	for (now = T; now != S; now = edge[pre[now]^1].to) {
		edge[pre[now]].r -= a[T];
		edge[pre[now]^1].r += a[T];
	}
	return true;
}
int MCMF() {
	int flow = 0, cost = 0;
	while (SPFA(flow, cost));
	printf("%d %d", flow/2, -cost/2);
}
int gcd(int x, int y) {
	if (y == 0) return x;
	return gcd(y, x % y);
}
bool check(int x, int y) {
	int z = sqrt(x*x-y*y);
	return (gcd(z, y) == 1 && z*z==x*x-y*y);
}
int main() {
	N = getint(); M = getint();
	S = 0; T = 2016;
	for (int i = N; i <= M; ++i) {
		ins(S, i, 1, 0);
		ins(i+1005, T, 1, 0);
		for (int j = N; j < i; ++j) if (check(i, j)) { ins(i, j+1005, 1, -i-j); ins(j, i+1005, 1, -i-j); }
	}
	MCMF();
	return 0;
}