AtCoder Grand Contest 038题解(莫比乌斯反演,最小割)

又开了个坑。。。


C - LCMs

我能说这是原题吗?

最小公倍数之和

发现题目所求的式子即为 \(\frac{1}{2}\times(\sum_{i=1}^n\sum_{j=1}^nlcm(a_i,a_j)-\sum_{i=1}^na_i)\)

然后按照上面的式子

F - Two Permutations

这道最小割好妙啊。

我们先发现一个排列的性质:对于排列上的每个环,如果其中一个点转了,为了满足排列的性质,这个环上的点都会转。

那么我们把每个环所成一个点(因为这里的点要不是全部转,要不全部等于 \(i\)),那么我们搞两个集合 \(S,T\),表示如果 \(p\) 不旋转就放入 \(S\)\(q\) 不旋转就放入 \(T\),中间建边,我们让最小割数为 \(A_i=B_i\) 的数,从源点 \(S\) 连向 \(p\) 点的边表示 \(p\) 不转,从 \(q\) 点连向汇点 \(t\) 的边表示 \(q\) 不转。接下来分类讨论:

  1. 如果 \(p_i=q_i=i\) 那么无论转不转都会产生贡献,直接减一

  2. 如果 \(p_i=i,q_i\neq i\),那么如果 \(q\) 不旋转就会产生贡献,连向 \(T\) 一条边。

  3. 如果 \(p_i\neq i,q_i=i\),那么如果 \(p\) 不旋转就会产生贡献,\(S\) 连向其一条边。

  4. 如果 \(p_i\neq q_i\),那么都不旋转才会产生贡献,从 \(q\) 连向 \(p\) 一条边(q 连向了 t,s 连向了 p)。

  5. 如果 \(p_i=q_i\) 那么转不转都会产生贡献,\(p\rightarrow q,q\rightarrow p\)

最后两种情况为什么是在之间连边?因为这样可以割一条边,使得不连通(相当于割两条边,这样会产生冗余操作)

#include <bits/stdc++.h>
using namespace std;
template <typename T>
void read(T &num) {
	T flag = 1;
	char ch = getchar();
	for (; '0' > ch || ch > '9'; ch = getchar())
		if (ch == '-') 
			flag = -1;
	for (num = 0; '0' <= ch && ch <= '9'; ch = getchar())
		num = num * 10 + ch - '0';
	num *= flag;
}
struct node{
	int pre, to, val;
}edge[2000005];
int head[200010], tot = 1;
void add_edge(int u, int v, int l) {
	edge[++tot] = node{head[u], v, l};
	head[u] = tot;
}
int m, n;
int S, T;
int d[200010], cur[200010];
bool bfs() {
	for (int i = 0; i <= T; i++) d[i] = 0, cur[i] = head[i];
	queue<int> q;
	q.push(S);
	d[S] = 1;
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		for (int i = head[x]; i; i = edge[i].pre) {
			int y = edge[i].to;
			if (edge[i].val > 0 && !d[y]) {
				d[y] = d[x] + 1;
				q.push(y);
			}
		}
	}
	return d[T];
}
int dinic(int x, int flow) {
	if (x == T || flow == 0) return flow;
	int rest = flow;
	for (int &i = cur[x]; i; i = edge[i].pre) {
		int y = edge[i].to;
		if (edge[i].val > 0 && rest > 0 && d[y] == d[x] + 1) {
			int k = dinic(y, min(edge[i].val, rest));
			if (!k) d[y] = 0;
			edge[i].val -= k;
			edge[i ^ 1].val += k;
			rest -= k;
			if (!rest) break;
		}
	}
	return flow - rest;
}
int p[100005], q[100005];
int idp[100005], idq[100005], cnt;
int main() {
	read(n);
	for (int i = 1; i <= n; i++) {
		read(p[i]);
		p[i]++;
	}
	for (int i = 1; i <= n; i++) {
		read(q[i]);
		q[i]++; 
	}
	cnt = 0;
	for (int i = 1; i <= n; i++) {
		if (!idp[i]) {
			cnt++;
			int now = p[i];
			idp[i] = cnt;
			while (now != i) {
				idp[now] = cnt;
				now = p[now];
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		if (!idq[i]) {
			cnt++;
			int now = q[i];
			idq[i] = cnt;
			while (now != i) {
				idq[now] = cnt;
				now = q[now];
			}
		}
	}
	cerr<<"zcr\n";
	int total = n;
	S = 0, T = cnt + 1;
	for (int i = 1; i <= n; i++) {
		if (p[i] == q[i] && p[i] == i) {
			total--;
		} else if (p[i] == i && q[i] != i) {
			add_edge(idq[i], T, 1);
			add_edge(T, idq[i], 0);
		} else if (p[i] != i && q[i] == i) {
			add_edge(S, idp[i], 1);
			add_edge(idp[i], S, 0);
		} else if (p[i] == q[i]) {
			add_edge(idp[i], idq[i], 1);
			add_edge(idq[i], idp[i], 0);
			add_edge(idp[i], idq[i], 0);
			add_edge(idq[i], idp[i], 1);
		} else {
			add_edge(idp[i], idq[i], 0);
			add_edge(idq[i], idp[i], 1);
		}
	}
	int flow, ans = 0;
	while (bfs()) {
		while ((flow = dinic(S, 0x7f7f7f7f)) > 0) {
			ans += flow;
		}
	}
	printf("%d", total - ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章