CF1292D Chaotic V.
题目链接
\(\Rightarrow \rm Codeforce\) 链接
题解
究极缝合题,可用于复习基础知识(雾)
首先,答案的节点的选取,一定是在所有关键点组成的虚树上,至于哪一个,可以用换根 \(\rm dp\) 求出。
接下来解决求 \(\rm lca\) 和距离的问题。
首先是求 \(\rm lca\) ,根据定义,从根,即节点 \(1\) 到某个节点 \(u\) 的路径的节点编号,一定是根据 \(u\) 的质因数分解,从较大的质因子开始一个一个加得到的,比如 \(1 \rightarrow 7 \rightarrow 7^2 \rightarrow 7^2\times 5 \rightarrow 7^2 \times 5\times 2 \rightarrow 7^2 \times 5\times 2^2\)。也就是说,要求出 \(\rm lca\) ,也就是要求出两个数质因数分解序列按 \(p\) 从大到小排序之后类似 \(\rm lcp\) 的东西。同时,启示我们可以将关键点按照 \(k\) 从小到大排序,等价于按 \(\rm dfs\) 序排序。
接下来是距离,因为求虚树上的边权只需要求点 \(u\) 到祖先 \(g\) 的距离,那么可以将 \(u\) 的所有指数加起来,然后减去 \(g\) 的所有指数的和,剩下来的就是距离。
至于求质因数分解,是经典的阶乘分解。
分析一下时间复杂度,显然虚树节点个数是 \(O(\max k_i)\) 的,瓶颈在求虚树的部分,求一次 \(\rm lca\) 和距离是 \(O(|\mathbb{P}|)\) (\(\mathbb{P}\) 是范围内的质数个数),因此时间复杂度大概是 \(O(\max k_i |\mathbb{P}|)\) 的,而 \(|\mathbb{P}|\leq 669\) 。
参考代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6 + 5, maxm = 5e3 + 5;
const LL INF = 1e18;
int n, idk[maxn];
struct Edge {
int v, nex; LL w;
Edge(int v = 0, int nex = 0, LL w = 0) : v(v), nex(nex), w(w) {}
} E[maxn];
int hd[maxm << 1], tote;
void addedge(int u, int v, LL w) {
E[++tote] = Edge(v, hd[u], w), hd[u] = tote;
E[++tote] = Edge(u, hd[v], w), hd[v] = tote;
}
inline bool pdif(int p1, int c1, int p2, int c2) { return p1 != p2 || c1 != c2; }
struct PFac {
vector<int> p, c;
bool operator < (const PFac& b) const {
for (int i = 0; i < min(b.p.size(), p.size()); i++)
if (pdif(p[i], c[i], b.p[i], b.c[i]))
return (p[i] != b.p[i] ? p[i] < b.p[i] : c[i] < b.c[i]);
return p.size() < b.p.size();
}
bool operator == (const PFac& b) const {
if (p.size() ^ b.p.size()) return false;
for (int i = 0; i < p.size(); i++)
if (pdif(p[i], c[i], b.p[i], b.c[i])) return false;
return true;
}
} dv[maxm];
PFac getlca(PFac& a, PFac& b) {
PFac res;
for (int i = a.p.size() - 1, j = b.p.size() - 1; (~i) && (~j); i--, j--) {
if (a.p[i] != b.p[j]) break;
res.p.push_back(a.p[i]);
if (a.c[i] != b.c[j]) { res.c.push_back(min(a.c[i], b.c[j])); break; }
res.c.push_back(a.c[i]);
}
reverse(res.p.begin(), res.p.end()), reverse(res.c.begin(), res.c.end());
return res;
}
//judge if u is in v's subtree
bool jdg_intr(PFac& u, PFac& v) {
if (u.p.size() < v.p.size()) return false;
for (int i = u.p.size() - 1, j = v.p.size() - 1; (~i) && (~j); i--, j--) {
if (pdif(u.p[i], u.c[i], v.p[j], v.c[j])) {
if (!j && u.p[i] == v.p[j] && u.c[i] >= v.c[j]) return true;
return false;
}
}
return true;
}
int dist(PFac& u, PFac& fa) {
int res = 0;
for (int i = 0; i < u.p.size(); i++) res += u.c[i];
for (int i = 0; i < fa.p.size(); i++) res -= fa.c[i];
return res;
}
map<PFac, int> idmp;
int idcnt;
int getid(PFac& vl) {
if (!idmp.count(vl)) return idmp[vl] = ++idcnt;
return idmp[vl];
}
LL w[maxm << 1], sw[maxm << 1], f[maxm << 1], g[maxm << 1];
PFac stk[maxm << 1]; int idv[maxm], istk[maxm << 1], stktp;
void ins(int id) {
if (!stktp) { stk[stktp = 1] = dv[id], istk[1] = idv[id]; return ; }
PFac l = getlca(stk[stktp], dv[id]);
int lid = getid(l), uid = idv[id];
if (lid != istk[stktp]) {
while (stktp > 1 && jdg_intr(stk[stktp - 1], l))
addedge(istk[stktp], istk[stktp - 1], dist(stk[stktp], stk[stktp - 1])),
stktp--;
if (istk[stktp] != lid)
addedge(istk[stktp], lid, dist(stk[stktp], l)),
stk[stktp] = l, istk[stktp] = lid;
}
stk[++stktp] = dv[id], istk[stktp] = uid;
}
void build_lst() {
for (int i = 1; i < stktp; i++) addedge(istk[i + 1], istk[i], dist(stk[i + 1], stk[i]));
}
void dfs1(int u, int fa) {
sw[u] += w[u];
for (int i = hd[u]; i; i = E[i].nex) {
int v = E[i].v;
if (v == fa) continue;
dfs1(v, u);
sw[u] += sw[v];
f[u] += f[v] + sw[v] * E[i].w;
}
}
void dfs2(int u, int fa) {
for (int i = hd[u]; i; i = E[i].nex) {
int v = E[i].v;
if (v == fa) continue;
g[v] = g[u] - sw[v] * E[i].w + (sw[1] - sw[v]) * E[i].w;
dfs2(v, u);
}
}
int cnt[maxm], pri[maxm], vis[maxm], pricnt;
void getpri(int n) {
for (int i = 2; i <= n; i++) if (!vis[i])
for (int j = 2; j * i <= n; j++) vis[i * j] = 1;
for (int i = 2; i <= n; i++) if (!vis[i]) pri[++pricnt] = i;
}
void div_fac(int n) {
for (int i = 1; pri[i] <= n && i <= pricnt; i++) {
int c = 0, now = n;
while (now) c += now / pri[i], now /= pri[i];
dv[n].p.push_back(pri[i]), dv[n].c.push_back(c);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &idk[i]), idk[i] = max(idk[i], 1), cnt[idk[i]]++;
sort(idk + 1, idk + 1 + n);
getpri(idk[n]);
for (int i = 1; i <= idk[n]; i++) if (cnt[i] || i == 1)
div_fac(i), idv[i] = getid(dv[i]), w[idv[i]] = cnt[i], ins(i);
build_lst();
dfs1(1, 0), g[1] = f[1], dfs2(1, 0);
LL ans = INF;
for (int i = 1; i <= idcnt; i++) ans = min(ans, g[i]);
printf("%lld\n", ans);
return 0;
}