It is my first Codeforces round!
Problem C
题目大意:一个平面内有一些无限长的直线,把平面分成了若干块,从一块走一步可以走到与它有公共边的另一块,但是只有公共点不能一步走过去。给定两个在块内部的点,问从S点到T点最少走几步。
题目分析:由于每步只能跨越公共边,不能从两直线交点处跨越,所以一步只能越过 S 与 T 之间的一条直线,那么答案就是 S 与 T 之间有多少条直线(即 S 与 T 在这些直线的两侧)。判断 S 与 T 在直线 l 两侧 : l : ax + by + c = 0 (a*Sx + b*Sy + c) 与 (a*Tx + b*Ty + c) 异号。
代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int MaxN = 300 + 5; int Sx, Sy, Tx, Ty, n, Ans, a, b, c; typedef long long LL; LL N1, N2; int main() { scanf("%d%d%d%d", &Sx, &Sy, &Tx, &Ty); scanf("%d", &n); Ans = 0; for (int i = 1; i <= n; ++i) { scanf("%d%d%d", &a, &b, &c); N1 = (LL)a * (LL)Sx + (LL)b * (LL)Sy + (LL)c; N2 = (LL)a * (LL)Tx + (LL)b * (LL)Ty + (LL)c; if (N1 > 0) N1 = 1; else N1 = -1; if (N2 > 0) N2 = 1; else N2 = -1; if (N1 * N2 < 0) ++Ans; } printf("%d ", Ans); return 0; }
Problem E
题目大意:
给定一个序列 A[n] ,还有一些实数对 (i, j) ,满足 i + j 为奇数。每次可以进行一次操作:选取一个实数对 (i, j),如果 A[i] 和 A[j] 不互质,就可以 ++Ans,然后将他们同除以一个大于 1 的公因子。这些实数对可重复利用。问 Ans 最多可以多大。
题目分析:
题目中的实数对为 (i, j) 满足 i + j 为奇数,所以 i 与 j 一个是奇数,一个是偶数。那么所有的实数对就相当于在每对 i,j 之间的连边,这样就组成一个二分图,只有下标为偶数的数和下标为奇数的数可能连边,奇数与奇数,偶数与偶数不能连边。
这个题的操作描述中的网络流气息十分明显...那么就是求最大流,下面我们来建图:
如果各种不同的质因数一起考虑会很困难,我想了好久最终弃了... 后来不得不求助于黄学长。
但是,每个质因子之间是互不影响的,如果我们每次只考虑一个质因数,那就简单多了。
对于一个质因数 x ,枚举所有的 A[i],如果 A[i] 中没有 x 这个因子,就跳过,否则分情况建边:若 i 为奇数,就连边 S -> i, 容量为 A[i] 中 x 的指数;如果 i 为偶数,就连边 i -> T, 容量为 A[i] 中 x 的指数。
对于每个实数对,我们从奇数向偶数连边,容量 INF。
这样求最大流就可以求出用这个质因子可以贡献的答案。
因此,我们每次处理一个质因数,每次重新建图,每次跑一遍最大流,累加到答案中。
代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int MaxX = 100 + 5, LogN = 32 + 5, MaxN = 100 + 5, MaxM = 300 + 5, INF = 0x3fffffff; int n, m, MaxFlow, Ans, S, T, Tot, a, b; int A[MaxX], d[MaxN], Num[MaxN], B[MaxN][3]; inline int gmin(int a, int b) {return a < b ? a : b;} inline int gmax(int a, int b) {return a > b ? a : b;} struct Edge { int v, w; Edge *Next, *Other; } E[MaxM * 2], *P = E, *Point[MaxN], *Last[MaxN]; inline void AddEdge(int x, int y, int z) { Edge *Q = ++P; ++P; P -> v = y; P -> w = z; P -> Next = Point[x]; Point[x] = P; P -> Other = Q; Q -> v = x; Q -> w = 0; Q -> Next = Point[y]; Point[y] = Q; Q -> Other = P; } int DFS(int Now, int Flow) { if (Now == T) return Flow; int ret = 0; for (Edge *j = Last[Now]; j; j = j -> Next) { if (j -> w && d[Now] == d[j -> v] + 1) { Last[Now] = j; int p = DFS(j -> v, gmin(j -> w, Flow - ret)); ret += p; j -> w -= p; j -> Other -> w += p; if (ret == Flow) return ret; } } if (d[S] >= Tot) return ret; if (--Num[d[Now]] == 0) d[S] = Tot; ++Num[++d[Now]]; Last[Now] = Point[Now]; return ret; } void Solve(int x) { memset(E, 0, sizeof(E)); P = E; memset(Point, 0, sizeof(Point)); for (int i = 1; i <= n; ++i) { int Cnt = 0; while (A[i] % x == 0) { ++Cnt; A[i] /= x; } if (Cnt == 0) continue; if (i & 1) AddEdge(S, i, Cnt); else AddEdge(i, T, Cnt); } for (int i = 1; i <= m; ++i) AddEdge(B[i][0], B[i][1], INF); MaxFlow = 0; memset(d, 0, sizeof(d)); memset(Num, 0, sizeof(Num)); Num[0] = Tot; for (int i = 1; i <= Tot; ++i) Last[i] = Point[i]; while (d[S] < Tot) MaxFlow += DFS(S, INF); Ans += MaxFlow; } int main() { scanf("%d%d", &n, &m); Ans = 0; for (int i = 1; i <= n; ++i) scanf("%d", &A[i]); for (int i = 1; i <= m; ++i) { scanf("%d%d", &B[i][0], &B[i][1]); if (B[i][1] & 1) swap(B[i][0], B[i][1]); } S = n + 1; T = S + 1; Tot = T; for (int i = 1; i <= n; ++i) { int SQ = (int)sqrt(A[i] * 1.0); for (int j = 2; j <= SQ; ++j) if (A[i] % j == 0) Solve(j); if (A[i] > 1) Solve(A[i]); } printf("%d ", Ans); return 0; }