CF1292D Chaotic V.

题目链接

\(\Rightarrow \rm luogu\) 链接

\(\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;
}
posted @ 2022-03-11 22:19  juruohjr  阅读(29)  评论(0编辑  收藏  举报