又開了個坑。。。
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\) 不轉。接下來分類討論:
-
如果 \(p_i=q_i=i\) 那麼無論轉不轉都會產生貢獻,直接減一
-
如果 \(p_i=i,q_i\neq i\),那麼如果 \(q\) 不旋轉就會產生貢獻,連向 \(T\) 一條邊。
-
如果 \(p_i\neq i,q_i=i\),那麼如果 \(p\) 不旋轉就會產生貢獻,\(S\) 連向其一條邊。
-
如果 \(p_i\neq q_i\),那麼都不旋轉纔會產生貢獻,從 \(q\) 連向 \(p\) 一條邊(q 連向了 t,s 連向了 p)。
-
如果 \(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;
}