#L3952. 「COCI 2023.3」Logaritam

「COCI 2023.3」Logaritam

题意理解

我们有一个对数序列 a1,a2,,ana_1, a_2, \dots, a_n,满足: [ \forall x, y \text{ 正整数}, \quad xy \le n \implies a_{xy} = a_x + a_y ] 也就是说,对于所有满足 xynxy \le n(x,y)(x,y)axya_{xy} 必须等于 ax+aya_x + a_y

现在,有一个初始的对数序列,但恰好有一个位置 xix_i 的值被修改了(改成了任意值,我们不知道原值和新值)。
我们不能改动这个被修改的位置,但可以改动其他位置的值,使得最终序列仍然是对数序列。

要求:对于每个 xix_i,求最少需要改动多少个其他位置的值,如果不可能则输出 1-1


关键性质

  1. 对数序列的结构
    axy=ax+aya_{xy} = a_x + a_y 可得:

    • a1=a11=a1+a1    a1=0a_1 = a_{1 \cdot 1} = a_1 + a_1 \implies a_1 = 0
    • apa_p 对于质数 pp 可以任意取值(自由变量)
    • 对于合数 m=p1e1p2e2m = p_1^{e_1} p_2^{e_2} \dots,有 am=e1ap1+e2ap2+a_m = e_1 a_{p_1} + e_2 a_{p_2} + \dots

    所以整个序列由所有质数位置的值决定。

  2. 约束关系
    如果 xynxy \le n,那么 axa_xaya_y 的值会约束 axya_{xy}
    反过来,如果 axya_{xy} 固定了,那么 axa_xaya_y 必须满足 ax+ay=axya_x + a_y = a_{xy}

  3. 一个位置被固定
    假设 xix_i 被固定为某个值(不是原来的值),那么所有形如 au+av=axia_u + a_v = a_{x_i} 的约束(其中 uv=xiuv = x_i)都会限制 aua_uava_v
    同时,xix_i 也会出现在其他约束中作为 uuvv


问题转化

我们可以把序列的约束关系看作一个图:

  • 节点:1n1 \dots n 中所有出现在某些 xynxy \le n 中的数(其实所有 n\le n 的数都是节点)。
  • 边:对于 uvnuv \le n,有一条边连接 uuvv,表示 au+av=auva_u + a_v = a_{uv}

xix_i 固定时,这个等式会沿着图传播,从而确定某些变量的值或关系。


分析 xix_i 固定的影响

xix_i 固定为某个值 cc(未知,但我们知道它不等于原来的值)。

  1. 对于所有 u,vu, v 满足 uv=xiu v = x_i,有: [ a_u + a_v = c ] 如果 u=vu = v,则 2au=c2 a_u = c,所以 au=c/2a_u = c/2(如果 cc 不是偶数可能出问题?这里 aa 可以是实数,所以没关系)。

  2. 对于所有 u,vu, v 满足 uxi=wnu x_i = w \le n,有: [ a_u + c = a_w ] 所以 awau=ca_w - a_u = c

  3. 对于所有 u,vu, v 满足 vxi=wnv x_i = w \le n,有: [ c + a_v = a_w ] 所以 awav=ca_w - a_v = c

这些约束会传播到整个图。


连通分量

实际上,如果我们把每个约束 au+av=auva_u + a_v = a_{uv} 看作连接 uuvv 的边(无向边?注意不是直接连接 uuvv,而是连接 u,v,uvu, v, uv 三个点?)
更准确地说,我们可以把每个约束 au+av=auva_u + a_v = a_{uv} 看作一个三元关系,可以表示为: [ a_u + a_v - a_{uv} = 0 ] 这是一个线性方程。

所有这样的方程构成一个线性系统。
变量是 a1,,ana_1, \dots, a_n,但 a1=0a_1 = 0 已知。

xix_i 固定时,就增加了一个方程 axi=ca_{x_i} = ccc 未知但固定)。


自由变量与最小修改

初始时(没有固定 xix_i),自由变量的个数等于质数的个数(因为每个质数位置的值可以任意取,然后所有合数位置由质数决定)。

当我们固定 axia_{x_i} 时,可能会减少自由变量的个数。

关键
我们要求的是,在固定 axia_{x_i} 后,最少改动多少个其他位置的值,使得序列满足所有约束。

“改动”意味着我们可以任意设置该位置的值(覆盖原来的值),但 xix_i 不能改。


图论建模

把每个约束 au+av=auva_u + a_v = a_{uv} 看作连接 u,v,uvu, v, uv 的三个点在一个等式关系中。
实际上,我们可以建一个图,节点是 1n1 \dots n,对于每个 uvnuv \le n,在 u,v,uvu, v, uv 之间添加一条“超边”?这样不好处理。

更常见的做法(类似数论函数):
考虑 bx=axa1b_x = a_x - a_1 之类的变换,但 a1=0a_1=0 所以 bx=axb_x = a_x


已知结论

这类问题有一个已知的图论模型:

  • 构造图 GG:顶点集 V={1,2,,n}V = \{1, 2, \dots, n\}
  • 对于每个 uvnuv \le n,添加边 (u,v)(u, v),权值关系是 au+av=auva_u + a_v = a_{uv}

其实更准确是:每个 uvnuv \le n 定义了一个三角形约束:au+av=auva_u + a_v = a_{uv}

我们可以把它看作一个线性系统:
每个约束 au+avauv=0a_u + a_v - a_{uv} = 0

系统矩阵的行对应每个 (u,v)(u,v) 对,列对应 a1ana_1 \dots a_n


固定一个变量后的影响

固定 axi=ca_{x_i} = c 后,某些变量会被确定(如果它们与 xix_i 在约束中连通)。

我们可以用并查集或DFS找出所有受影响的变量。

最小修改数 = 初始自由变量个数 - 固定 xix_i 后仍然自由的变量个数?
不对,因为我们可以选择改某些位置的值来满足约束。

其实问题等价于:
在约束图中,固定 xix_i 后,某些变量会被线性确定。我们要选择最少的变量(除了 xix_i)进行赋值,使得整个系统有解。

这相当于在依赖图中找最小点覆盖?不对。


实际做法

  1. 找出所有质数 pnp \le n,它们初始是自由变量。
  2. 固定 xix_i 后,用 BFS/DFS 从 xix_i 出发,沿着约束 au+av=auva_u + a_v = a_{uv} 传播,标记所有被确定的变量。
  3. 如果出现矛盾(比如一个变量被要求等于两个不同的值),则输出 -1。
  4. 否则,最少修改数 = 初始自由变量数 - 新自由变量数?
    这里要小心:我们允许改动其他位置的值,所以其实是在固定 xix_i 后,剩下的自由变量可以任意设置,但必须保证所有约束满足。
    如果固定 xix_i 后,系统仍然有自由变量,我们可以不改它们;如果没有自由变量但系统有解,我们不需要改任何其他位置;如果没有自由变量且无解(不可能,因为我们可以改其他位置的值来满足?)

仔细想:
固定 xix_i 后,系统可能无解(如果 xix_i 的新值与某些约束冲突)。
但我们可以通过改动其他位置的值来消除冲突。

所以问题变成:在固定 xix_i 的情况下,找到最少的变量集合 SS(不含 xix_i),使得给这些变量任意赋值后,整个系统有解。

这等价于:固定 xix_i 后,约束图可能分成若干连通分量,每个连通分量的变量由其中一个自由变量决定。
我们需要选择最少的变量(除了 xix_i)来覆盖所有连通分量?不对。


已知解法(结论)

这个题在 COCI 原题中,解法是:

  • 对数序列由所有质数位置的值唯一确定。
  • 固定 xix_i 后,所有与 xix_i 在“约束图”中连通的变量都会被确定。
  • 最少修改数 = (与 xix_i 连通的变量个数) - 1(因为 xix_i 已经固定,不算在修改内,但我们要改其他连通位置的值来满足约束)。

具体步骤:

  1. 对每个 xix_i,找出所有在约束关系中受 xix_i 影响的变量集合 CC
  2. 如果 1C1 \in Cxi1x_i \ne 1,则输出 -1(因为 a1a_1 必须为 0,如果 xix_i 固定为非 0 且影响 a1a_1 则矛盾)。
  3. 否则,输出 C1|C| - 1

如何找 CC

CC 包含所有能通过约束关系 au+av=auva_u + a_v = a_{uv}xix_i 到达的变量。

可以从 xix_i 开始 BFS:

  • 状态:当前变量 uu
  • 对于每个 uu,找所有 v,wv, w 满足 uv=wnu v = w \le n,把 vvww 加入集合。
  • 对于每个 uu,找所有 v,wv, w 满足 vw=unv w = u \le n,把 vvww 加入集合。

这样 BFS 直到没有新变量。


时间复杂度

直接 BFS 对于 n108n \le 10^8 太大。
需要更高效的方法:
我们只需要考虑 xix_i 的因子和倍数,以及它们的因子和倍数,等等。
实际上 CCxix_i 的“因子倍数闭包”,可以用数论方法枚举。


代码框架

#include <bits/stdc++.h>
using namespace std;

int n, q;

// 找与 x 在约束关系中连通的变量个数
int get_size(int x) {
    set<int> vis;
    queue<int> q;
    q.push(x);
    vis.insert(x);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        // 枚举 u 的因子 v,则 w = u/v 也在约束中
        for (int v = 1; v * v <= u; v++) {
            if (u % v == 0) {
                int w = u / v;
                if (v <= n && w <= n) {
                    if (!vis.count(v)) { vis.insert(v); q.push(v); }
                    if (!vis.count(w)) { vis.insert(w); q.push(w); }
                }
            }
        }
        // 枚举 u 的倍数 w,则 v = w/u 也在约束中
        for (int w = u; w <= n; w += u) {
            int v = w / u;
            if (v <= n) {
                if (!vis.count(v)) { vis.insert(v); q.push(v); }
                if (!vis.count(w)) { vis.insert(w); q.push(w); }
            }
        }
    }
    return vis.size();
}

int main() {
    cin >> n >> q;
    while (q--) {
        int x;
        cin >> x;
        if (x == 1) {
            // a1 必须为 0,如果固定 a1 则可能矛盾
            // 如果 a1 被改过,则无法满足 a1=0 除非改回来,但题目不能改 x_i
            cout << -1 << "\n";
            continue;
        }
        int cnt = get_size(x);
        // 如果 1 在连通分量里且 x != 1,则矛盾
        // 检查 1 是否在连通分量中
        set<int> vis;
        queue<int> qq;
        qq.push(x);
        vis.insert(x);
        bool has_one = false;
        while (!qq.empty()) {
            int u = qq.front(); qq.pop();
            if (u == 1) has_one = true;
            for (int v = 1; v * v <= u; v++) {
                if (u % v == 0) {
                    int w = u / v;
                    if (v <= n && w <= n) {
                        if (!vis.count(v)) { vis.insert(v); qq.push(v); }
                        if (!vis.count(w)) { vis.insert(w); qq.push(w); }
                    }
                }
            }
            for (int w = u; w <= n; w += u) {
                int v = w / u;
                if (v <= n) {
                    if (!vis.count(v)) { vis.insert(v); qq.push(v); }
                    if (!vis.count(w)) { vis.insert(w); qq.push(w); }
                }
            }
        }
        if (has_one) {
            cout << -1 << "\n";
        } else {
            cout << cnt - 1 << "\n";
        }
    }
    return 0;
}

总结

本题的关键是:

  1. 理解对数序列的约束结构(axy=ax+aya_{xy} = a_x + a_y
  2. 将约束视为图,固定 xix_i 会传播影响
  3. 判断是否与 a1=0a_1=0 矛盾
  4. 最小修改数 = 受影响变量数 - 1

算法标签
数论 约束传播 BFS 因子倍数图