1. 程式人生 > >【U21403】倍數變換

【U21403】倍數變換

bsp spa memset ons ems pre 但是 namespace problem

https://www.luogu.org/problemnew/show/U21403

有標號為 $1, 2, ..., n$ 的小球,每個小球的標號 $x$ 可以替換為任意一個 $x$ 的倍數,請你找到一種變換,使得【標號 $x$ 的出現次數為奇數】的情況盡可能少。

$n \le 10 ^ 3$

相當於可以將小球劃分到任意一個其倍數的集合內,使得大小為奇數的集合盡可能少,即

$$\sum_x (x \mod 2)$$

變形為

$$\sum_x (x - 2 \lfloor \frac{x}{2} \rfloor) = n - 2 \sum_x \lfloor \frac{x}{2} \rfloor$$

暴力的想法是對同一個集合內的點兩兩連邊,成為一個團,跑一般圖最大匹配,但是會 TLE 。

考慮優化建圖,對每個集合,把集合中的點排成一個環,在兩個相鄰的點之間建立一個輔助點,輔助點之間連邊成環,一個輔助點與相鄰兩個點連邊。若這個塊剩下 $x$ 個點,那麽這個塊的最大匹配為 $\lfloor \frac{x + k}{2} \rfloor = \lfloor \frac{x}{2} \rfloor - \frac{k}{2}$ 。

#include <bits/stdc++.h>
using namespace std;
#define F(i, s, t) for (int i = (s), _ = (t); i <= _; i ++)
#define
Fo(i, s, t) for (int i = (s), _ = (t); i < _; i ++) #define pb push_back const int N = 20000; int n, tot; vector<int> li[N], gr[N]; int ans, mat[N], u[N]; void Init(int x, int y) { gr[x].pb(y), gr[y].pb(x); } int DFS(int x) { u[x] = 1; for (int v : gr[x]) { int w = mat[v];
if (! w) return mat[x] = v, mat[v] = x, 1; if (! u[w]) { mat[x] = v, mat[v] = x, mat[w] = 0; if (DFS(w)) return 1; mat[x] = 0, mat[v] = w, mat[w] = v; } } return 0; } int main() { #ifndef ONLINE_JUDGE //freopen("403.in", "r", stdin); #endif cin >> n, tot = n; F(i, 1, n) for (int j = i; j <= n; j += i) li[j].pb(i); F(cen, 1, n) { int s = li[cen].size(), c = s + (s & 1); Fo(i, 0, c) Init(tot + 1 + i, tot + 1 + (i + 1) % c); Fo(i, 0, s) { Init(li[cen][i], tot + 1 + i); Init(li[cen][i], tot + 1 + (i + 1) % c); } tot += c; } ans = - (tot - n) >> 1; F(i, 1, tot) if (! mat[i]) { memset(u, 0, sizeof u); ans += DFS(i); } cout << n - 2 * ans << endl; return 0; }

【U21403】倍數變換