【AtCoder】AtCoder Grand Contest 038

比赛链接

点击打开链接

官方题解

点击打开链接

Problem A. 01 Matrix

将子矩阵 [ 1 , B ] × [ 1 , A ] [1,B]\times[1,A] [ B + 1 , H ] × [ A + 1 , W ] [B+1,H]\times[A+1,W] 标为 1 1 ,剩余部分标为 0 0 即可。

时间复杂度 O ( H × W ) O(H\times W)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int main() {
	int n, m, a, b;
	read(n), read(m), read(a), read(b);
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++)
			if (i <= b && j <= a) putchar('1');
			else if (i > b && j > a) putchar('1');
			else putchar('0');
		puts("");
	}
	return 0;
}

Problem B. Sorting a Segment

考虑排序后的结果,若所排序的区间原本就是有序的,那么得到的序列就是原序列,所有排序有序区间的方案对应的结果是相同的。

否则,一定改变了某些元素的位置,不难发现若排序 [ i , i + k ) [i,i+k) 的结果和排序 [ j , j + k ) [j,j+k) 的结果一样,那么排序 [ i , i + k ) , [ i + 1 , i + 1 + k ) , [ i + 2 , i + 2 + k ) , , [ j , j + k ) [i,i+k),[i+1,i+1+k),[i+2,i+2+k),\dots,[j,j+k) 的结果也一样,因此我们只需要统计排序 [ i 1 , i 1 + k ) , [ i , i + k ) [i-1,i-1+k),[i,i+k) 的结果不同的 i i 的个数即可。

显然,排序 [ i 1 , i 1 + k ) , [ i , i + k ) [i-1,i-1+k),[i,i+k) 的结果不同当且仅当 P i 1 P_{i-1} [ i 1 , i + k ) [i-1,i+k) 中的最小值, P i + k 1 P_{i+k-1} [ i 1 , i + k ) [i-1,i+k) 中的最大值,用 S T ST 表或是单调队列解决即可。

时间复杂度 O ( N ) O(N) O ( N L o g N ) O(NLogN)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
namespace rmq {
	const int MAXN = 2e5 + 5;
	const int MAXLOG = 20;
	int Max[MAXN][MAXLOG], Min[MAXN][MAXLOG], Log[MAXN];
	int queryMax(int l, int r) {
		int len = r - l + 1, tmp = Log[len];
		return max(Max[l][tmp], Max[r - (1 << tmp) + 1][tmp]);
	}
	int queryMin(int l, int r) {
		int len = r - l + 1, tmp = Log[len];
		return min(Min[l][tmp], Min[r - (1 << tmp) + 1][tmp]);
	}
	void init(int *a, int n) {
		for (int i = 1; i <= n; i++) {
			Min[i][0] = a[i];
			Max[i][0] = a[i];
			Log[i] = Log[i - 1];
			if ((1 << (Log[i] + 1)) <= i) Log[i]++;
		}
		for (int t = 1; t < MAXLOG; t++)
		for (int i = 1, j = (1 << (t - 1)) + 1; j <= n; i++, j++) {
			Max[i][t] = max(Max[i][t - 1], Max[j][t - 1]);
			Min[i][t] = min(Min[i][t - 1], Min[j][t - 1]);
		}
	}
}
int n, k, a[MAXN];
bool flg[MAXN];
int main() {
	read(n), read(k);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	rmq :: init(a, n);
	int ans = 0, now = 0;
	for (int i = 1; i <= k - 1; i++)
		now += a[i] < a[i + 1];
	for (int i = 1, j = k; j <= n; i++, j++) {
		if (now == k - 1) {
			ans = 1;
			flg[i] = true;
		}
		now -= a[i] < a[i + 1];
		now += a[j] < a[j + 1];
	}
	for (int i = 1, j = k; j <= n; i++, j++) {
		if (flg[i]) continue;
		if (i != 1 && a[i - 1] < rmq :: queryMin(i, j - 1) && a[j] > rmq :: queryMax(i, j - 1)) continue;
		else ans++;
	}
	writeln(ans);
	return 0;
}

Problem C. LCMs

考虑计算 i = 1 N j = 1 N L c m ( A i , A j ) \sum_{i=1}^{N}\sum_{j=1}^{N}Lcm(A_i,A_j) ,最后减去 i = 1 N A i \sum_{i=1}^{N}A_i ,再除以 2 2 得到答案。

M = 1 0 6 M=10^6 ,有
i = 1 N j = 1 N L c m ( A i , A j ) \sum_{i=1}^{N}\sum_{j=1}^{N}Lcm(A_i,A_j)
= i = 1 N j = 1 N A i × A j g c d ( A i , A j ) =\sum_{i=1}^{N}\sum_{j=1}^{N}\frac{A_i\times A_j}{gcd(A_i,A_j)}
= g = 1 M 1 g i = 1 N j = 1 N [ g c d ( A i , A j ) = g ] A i × A j =\sum_{g=1}^{M}\frac{1}{g}\sum_{i=1}^{N}\sum_{j=1}^{N}[gcd(A_i,A_j)=g]A_i\times A_j
= g = 1 M 1 g g A i g A j A i × A j g d A i , g d A j μ ( d ) =\sum_{g=1}^{M}\frac{1}{g}\sum_{g\mid A_i}\sum_{g\mid A_j}A_i\times A_j\sum_{gd\mid A_i,gd\mid A_j}\mu(d)
= g = 1 M 1 g d = 1 M g μ ( d ) g d A i g d A j A i × A j =\sum_{g=1}^{M}\frac{1}{g}\sum_{d=1}^{\frac{M}{g}}\mu(d)\sum_{gd\mid A_i}\sum_{gd\mid A_j}A_i\times A_j

在上式中枚举 g d gd 即可,需要计算 μ \mu 1 i d \frac{1}{id} 卷积后的结果,以及输入中是 i i 的倍数的数之和 v a l u e i value_i ,然后有
i = 1 N j = 1 N L c m ( A i , A j ) = g d = 1 M ( 1 i d μ ) ( g d ) × v a l u e g d 2 \sum_{i=1}^{N}\sum_{j=1}^{N}Lcm(A_i,A_j)=\sum_{gd=1}^{M}(\frac{1}{id}*\mu)(gd)\times value_{gd}^2

时间复杂度 O ( N + M L o g M ) O(N+MLogM)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
const int P = 998244353;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int tot, prime[MAXN], f[MAXN];
int miu[MAXN], inv[MAXN], func[MAXN];
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
int power(int x, int y) {
	if (y == 0) return 1;
	int tmp = power(x, y / 2);
	if (y % 2 == 0) return 1ll * tmp * tmp % P;
	else return 1ll * tmp * tmp % P * x % P;
}
void sieve(int n) {
	miu[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (f[i] == 0) {
			prime[++tot] = f[i] = i;
			miu[i] = P - 1;
		}
		for (int j = 1; j <= tot && prime[j] <= f[i]; j++) {
			int tmp = prime[j] * i;
			if (tmp > n) break;
			f[tmp] = prime[j];
			if (prime[j] == f[i]) miu[tmp] = 0;
			else miu[tmp] = (P - miu[i]) % P;
		}
	}
	for (int i = 1; i <= n; i++) {
		inv[i] = power(i, P - 2);
		for (int j = 1; i * j <= n; j++)
			update(func[i * j], 1ll * inv[i] * miu[j] % P);
	}
}
int a[MAXN], cnt[MAXN], value[MAXN];
int main() {
	int n; read(n);
	int m = 1e6; sieve(m);
	for (int i = 1; i <= n; i++)
		read(a[i]), cnt[a[i]]++;
	for (int i = 1; i <= m; i++) {
		for (int j = i; j <= m; j += i)
			update(value[i], 1ll * j * cnt[j] % P);
	}
	int ans = 0;
	for (int i = 1; i <= m; i++)
		update(ans, 1ll * value[i] * value[i] % P * func[i] % P);
	for (int i = 1; i <= n; i++)
		update(ans, P - a[i]);
	writeln(1ll * ans * inv[2] % P);
	return 0;
}

Problem D. Unique Path

首先特判 M = N 1 M=N-1 的情况,此时图是一棵树,不允许出现 C i = 1 C_i=1 的限制。

考虑 C i = 0 C_i=0 的限制,若将这些限制的 A i , B i A_i,B_i 看做边,将会得到若干个连通块,不难证明,在原图中,这些连通块的导出子图均为树,不妨先将这些树连出来。

令剩余边数为 M M' ,连通块数为 C n t Cnt ,由于 M N M\geq N M C n t M'\geq Cnt

对于 C i = 1 C_i=1 的限制,若连接了两个同一棵树中的节点,则答案为 N o No

否则,我们可以从每一棵树中取出一个点来连成一个环,以满足 C i = 1 C_i=1 的限制,但由于至多连成一个完全图,因此还需要满足 M ( C n t 2 ) M'\leq\binom{Cnt}{2}

C n t = 2 Cnt=2 的情况需要特判,此时不允许出现 C i = 1 C_i=1 的限制。

时间复杂度 O ( N + Q α ( N ) ) O(N+Q\alpha(N))

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int f[MAXN];
int F(int x) {
	if (f[x] == x) return x;
	else return f[x] = F(f[x]);
}
int x[MAXN], y[MAXN], type[MAXN];
int main() {
	int n, q; ll m;
	read(n), read(m), read(q);
	for (int i = 1; i <= n; i++)
		f[i] = i;
	for (int i = 1; i <= q; i++) {
		read(x[i]), read(y[i]), read(type[i]);
		x[i]++, y[i]++;
		if (type[i] == 0) {
			f[F(x[i])] = F(y[i]);
		} else {
			if (m == n - 1) {
				puts("No");
				return 0;
			}
		}
	}
	for (int i = 1; i <= q; i++)
		if (type[i] && F(x[i]) == F(y[i])) {
			puts("No");
			return 0;
		}
	int cnt = 0;
	for (int i = 1; i <= n; i++)
		cnt += F(i) == i;
	if (cnt <= 2) {
		for (int i = 1; i <= q; i++)
			if (type[i]) {
				puts("No");
				return 0;
			}
	}
	ll ans = n - 1 - (cnt - 1);
	ans += 1ll * cnt * (cnt - 1) / 2;
	if (ans >= m) puts("Yes");
	else puts("No");
	return 0;
}

Problem E. Gachapon

考虑计算到达各个尚未完成的状态的概率之和,由期望的线性性,这也是答案。

令所有状态的指数型生成函数为 G ( x ) G(x) ,普通型生成函数为 g ( x ) g(x) ,已经完成的状态的指数型生成函数为 F ( x ) F(x) ,普通型生成函数为 f ( x ) f(x) ,那么我们要求的就是 g ( 1 ) f ( 1 ) g(1)-f(1)

考虑计算 H ( x ) = G ( x ) F ( x ) H(x)=G(x)-F(x) ,令 p i = A i A i p_i=\frac{A_i}{\sum A_i} ,有
H ( x ) = e x i = 1 N ( e p i x 1 p i x p i 2 x 2 2 p i B i 1 x B i 1 ( B i 1 ) ! ) H(x)=e^x-\prod_{i=1}^{N}(e^{p_ix}-1-p_ix-\frac{p_i^2x^2}{2}-\dots-\frac{p_i^{B_i-1}x^{B_i-1}}{(B_i-1)!})

S A = A i , y = e 1 S A x SA=\sum A_i,y=e^{\frac{1}{SA}x} ,那么
H ( x ) = y S A i = 1 N ( y A i 1 p i x p i 2 x 2 2 p i B i 1 x B i 1 ( B i 1 ) ! ) H(x)=y^{SA}-\prod_{i=1}^{N}(y^{A_i}-1-p_ix-\frac{p_i^2x^2}{2}-\dots-\frac{p_i^{B_i-1}x^{B_i-1}}{(B_i-1)!})

暴力展开 H ( x ) H(x) 的计算式,对于各个 y i y^i ,展开关于 x x 的多项式的时间复杂度均为 O ( ( B i ) 2 ) O((\sum B_i)^2) ,因此展开的时间复杂度为 O ( ( A i ) × ( B i ) 2 ) O((\sum A_i)\times(\sum B_i)^2)

接下来考虑如何将 H ( x ) H(x) 转为普通型生成函数求出 h ( 1 ) h(1)

对于形如 c y i cy^i 的项,其对答案的贡献显然为
c 1 i S A \frac{c}{1-\frac{i}{SA}}

考虑形如 c y i x j cy^ix^j 的项,其对答案的贡献应当为
c k = 0 ( i S A ) k × ( k + 1 ) × ( k + 2 ) × × ( k + j ) c\sum_{k=0}^{\infty}(\frac{i}{SA})^k\times(k+1)\times(k+2)\times\dots\times(k+j)

考虑对于 0 < p < 1 , i N 0<p<1,i\in\mathbb{N} ,计算
S = k = 0 p k × k i S=\sum_{k=0}^{\infty}p^k\times k^i

等式两侧同时乘以 p p ,则有
p S = k = 0 p k + 1 × k i pS=\sum_{k=0}^{\infty}p^{k+1}\times k^i

那么
( 1 p ) S = [ i = 0 ] + k = 0 p k + 1 × ( ( k + 1 ) i k i ) (1-p)S=[i=0]+\sum_{k=0}^{\infty}p^{k+1}\times ((k+1)^i-k^i)

( k + 1 ) i (k+1)^i 用二项式定理展开,即可将问题递归至更小的规模。

本部分时间复杂度也为 O ( ( A i ) × ( B i ) 2 ) O((\sum A_i)\times(\sum B_i)^2)

总时间复杂度 O ( ( A i ) × ( B i ) 2 ) O((\sum A_i)\times(\sum B_i)^2)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 405;
const int P = 998244353;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, sa, sb, a[MAXN], b[MAXN];
int f[MAXN][MAXN], g[MAXN], coef[MAXN][MAXN];
int fac[MAXN], inv[MAXN], binom[MAXN][MAXN];
int power(int x, int y) {
	if (y == 0) return 1;
	int tmp = power(x, y / 2);
	if (y % 2 == 0) return 1ll * tmp * tmp % P;
	else return 1ll * tmp * tmp % P * x % P;
}
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
int main() {
	read(n), sa = 0, f[0][0] = 1;
	for (int i = 1; i <= n; i++) {
		read(a[i]), read(b[i]);
		sa += a[i], sb += b[i];
	}
	fac[0] = inv[0] = binom[0][0] = 1;
	for (int i = 1; i <= max(sa, sb); i++) {
		fac[i] = 1ll * fac[i - 1] * i % P;
		inv[i] = power(fac[i], P - 2);
		binom[i][0] = 1;
		for (int j = 1; j <= i; j++) {
			binom[i][j] = (binom[i - 1][j - 1] + binom[i - 1][j]) % P;
		}
	}
	for (int x = 1, s = 0; x <= n; s += b[x++]) {
		memset(g, 0, sizeof(g));
		int p = 1ll * a[x] * power(sa, P - 2) % P;
		for (int i = 0, now = 1; i <= b[x] - 1; i++, now = 1ll * now * p % P)
			g[i] = (P - 1ll * now * inv[i] % P) % P;
		for (int i = sa; i >= 0; i--) {
			static int tmp[MAXN];
			memset(tmp, 0, sizeof(tmp));
			for (int j = 0; j <= b[x]; j++)
			for (int k = 0; k <= s; k++) {
				update(tmp[j + k], 1ll * f[i][k] * g[j] % P);
			}
			memcpy(f[i], tmp, sizeof(tmp));
			if (i >= a[x]) {
				for (int j = 0; j <= s; j++)
					update(f[i][j], f[i - a[x]][j]);
			}
		}
	}
	assert(f[sa][0] == 1);
	int ans = 0;
	for (int i = 0; i <= sb; i++)
		update(ans, 1ll * f[0][i] * fac[i] % P);
	coef[0][0] = 1;
	for (int i = 1; i <= sb; i++)
	for (int j = i; j >= 0; j--) {
		coef[i][j] = 1ll * coef[i - 1][j] * i % P;
		if (j != 0) update(coef[i][j], coef[i - 1][j - 1]);
	}
	for (int i = 1; i <= sa - 1; i++) {
		static int res[MAXN];
		int p = 1ll * i * power(sa, P - 2) % P;
		memset(res, 0, sizeof(res)), res[0] = power((1 - p + P) % P, P - 2);
		for (int j = 1; j <= sb; j++) {
			int tmp = 0;
			for (int k = 0; k <= j - 1; k++)
				update(tmp, 1ll * binom[j][k] * res[k] % P);
			res[j] = 1ll * tmp * p % P * res[0] % P;
		}
		for (int j = 0; j <= sb; j++) {
			int tmp = 0;
			for (int k = 0; k <= j; k++)
				update(tmp, 1ll * res[k] * coef[j][k] % P);
			update(ans, 1ll * tmp * f[i][j] % P);
		}
	}
	writeln((P - ans) % P);
	return 0;
}

Problem F. Two Permutations

将输入的排列 P , Q P,Q 看做两个置换,则其各个环或是整体被改为连向自身,或是不作修改。

注意到时间限制对于 1 0 5 10^5 的数据规模来说异常地大,考虑用最小割解题。

对于一个位置 i i ,有如下几种情况:
( 1 ) (1) P i = Q i = i P_i=Q_i=i :此时无论是否改变都不会对答案造成影响。
( 2 ) (2) P i = Q i i P_i=Q_i\ne i :此时只改变 P , Q P,Q 中的一个可以使答案 + 1 +1
( 3 ) (3) P i = i Q i P_i=i\ne Q_i :此时改变 Q Q 会使得答案 1 -1
( 4 ) (4) Q i = i P i Q_i=i\ne P_i :此时改变 P P 会使得答案 1 -1
( 5 ) (5) P i Q i i ,,P_i,Q_i,i 互不相同:同时改变 P , Q P,Q 会使得答案 1 -1

由于要使用最小割解题,我们不希望出现增益的条件,考虑对 ( 2 ) (2) 稍作修改。
( 2 ) (2) P i = Q i i P_i=Q_i\ne i :此时不改变 P , Q P,Q ,或同时改变 P , Q P,Q 会使得答案 1 -1

对于 P P 中的点,令将其划分在 S S 集表示改变,划分在 T T 集表示不改变;对于 Q Q 中的点,令将其划分在 T T 集表示改变,划分在 S S 集表示不改变。

那么对于上面的情况,我们分别可以按照如下方式连边。
( 2 ) (2) P Q P\leftrightarrow Q
( 3 ) (3) S Q S\rightarrow Q
( 4 ) (4) P T P\rightarrow T
( 5 ) (5) P Q P\rightarrow Q

时间复杂度 O ( D i n i c ( N , N ) ) O(Dinic(N,N))

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int INF = 2e9;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
namespace NetworkFlow {
	const int INF = 2e9;
	const int MAXP = 5e5 + 5;
	struct edge {
		int dest, flow;
		unsigned pos;
	};
	vector <edge> a[MAXP];
	int tot, s, t, dist[MAXP];
	unsigned curr[MAXP];
	void addedge(int x, int y, int z) {
		a[x].push_back((edge) {y, z, a[y].size()});
		a[y].push_back((edge) {x, 0, a[x].size() - 1});
	}
	int dinic(int pos, int limit) {
		if (pos == t) return limit;
		int used = 0, tmp;
		for (unsigned &i = curr[pos]; i < a[pos].size(); i++)
			if (a[pos][i].flow != 0 && dist[pos] + 1 == dist[a[pos][i].dest] && (tmp = dinic(a[pos][i].dest, min(limit - used, a[pos][i].flow)))) {
				used += tmp;
				a[pos][i].flow -= tmp;
				a[a[pos][i].dest][a[pos][i].pos].flow += tmp;
				if (used == limit) return used;
			}
		return used;
	}
	bool bfs() {
		static int q[MAXP];
		int l = 0, r = 0;
		memset(dist, 0, sizeof(dist));
		dist[s] = 1, q[0] = s;
		while (l <= r) {
			int tmp = q[l];
			for (unsigned i = 0; i < a[tmp].size(); i++)
				if (dist[a[tmp][i].dest] == 0 && a[tmp][i].flow != 0) {
					q[++r] = a[tmp][i].dest;
					dist[q[r]] = dist[tmp] + 1;
				}
			l++;
		}
		return dist[t] != 0;
	}
	int flow() {
		int ans = 0;
		while (bfs()) {
			memset(curr, 0, sizeof(curr));
			ans += dinic(s, INF);
		}
		return ans;
	}
}
int n, a[MAXN], b[MAXN];
int va[MAXN], vb[MAXN];
int main() {
	int n; read(n);
	for (int i = 1; i <= n; i++)
		read(a[i]), a[i]++;
	for (int i = 1; i <= n; i++)
		read(b[i]), b[i]++;
	for (int i = 1; i <= n; i++) {
		if (va[i]) continue;
		int pos = i;
		while (va[pos] == 0) {
			va[pos] = i;
			pos = a[pos];
		}
	}
	for (int i = 1; i <= n; i++) {
		if (vb[i]) continue;
		int pos = i;
		while (vb[pos] == 0) {
			vb[pos] = i + n;
			pos = b[pos];
		}
	}
	int ans = 0;
	for (int i = 1; i <= n; i++)
		ans += a[i] != b[i];
	int s = NetworkFlow :: s = 2 * n + 1;
	int t = NetworkFlow :: t = 2 * n + 2;
	for (int i = 1; i <= n; i++) {
		if (a[i] == b[i]) {
			if (a[i] == i) continue; ans += 1;
			NetworkFlow :: addedge(vb[i], va[i], 1);
			NetworkFlow :: addedge(va[i], vb[i], 1);
		} else {
			if (a[i] == i) NetworkFlow :: addedge(s, vb[i], 1);
			else if (b[i] == i) NetworkFlow :: addedge(va[i], t, 1);
			else NetworkFlow :: addedge(va[i], vb[i], 1);
		}
	}
	ans -= NetworkFlow :: flow();
	writeln(ans);
	return 0;
}
发布了813 篇原创文章 · 获赞 93 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/101171588