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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章