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;
}