UVa 12858 Even Distribution

题目描述

Endre\texttt{Endre}Endre 有很多侄子和侄女。每年,他都会带其中一些孩子去一个群岛旅行,那里有一家船公司在一些岛屿之间提供双向服务。由于 Endre\texttt{Endre}Endre 和孩子们可以飞往或直接从任何岛屿返回,任何行程都可以描述为一个非空的岛屿序列 i1,i2,…,ini_1, i_2, \ldots, i_ni1,i2,,in,使得每对连续的岛屿 iji_jijij+1i_{j+1}ij+1 之间有船运服务。行程的第一个和最后一个岛屿可以是同一个,也可以不同,行程中可以多次访问同一个岛屿。

群岛中的每个岛屿都生产一种不同的特色糖果,并且每次会给到达的每个团体固定数量的糖果。Endre\texttt{Endre}Endre 自己不喜欢糖果,但孩子们几乎会立刻吃完。为了避免争吵,每次团队到达一个岛屿并收到糖果时,他会把糖果均匀地分给所有孩子。

你可能好奇 Endre\texttt{Endre}Endre 如何总能均匀分配他们在每个岛屿收到的糖果。其实答案很简单。每年,旅行社都会提前把行程计划(序列 i1,i2,…,ini_1, i_2, \ldots, i_ni1,i2,,in)发给他。由于他想带尽可能多的侄子和侄女,他计算出在遵守糖果均匀分配规则的前提下,他可以带的最大孩子数量 kkk。注意,每个行程计划唯一地决定了带孩子的人数。

这种情况已经持续了很多年,每次 Endre\texttt{Endre}Endre 最终带的孩子数量都不同。他想知道他能带多少种不同的孩子数量,也就是有多少个整数 kkk,使得存在一个行程计划,他最终会带 kkk 个孩子。现在 Endre\texttt{Endre}Endre 正忙于准备今年的旅行,你能帮他回答这个问题吗?

输入格式

输入包含多个测试用例;每个测试用例格式如下。第一行包含两个整数 IIISSS1≤I,S≤1041 \le I, S \le 10^41I,S104),分别表示岛屿数量和船运服务数量。岛屿用从 111III 的不同整数标识。第二行包含 III 个整数 C1,C2,…,CIC_1, C_2, \ldots, C_IC1,C2,,CI,其中 CiC_iCi 表示到达岛屿 iii 时团体收到的糖果数量(1≤Ci≤1051 \le C_i \le 10^51Ci105)。接下来的 SSS 行,每行描述一个不同的船运服务,包含两个整数 AAABBB1≤A<B≤I1 \le A < B \le I1A<BI),表示可以在岛屿 AAABBB 之间双向旅行。没有两对岛屿之间有重复的船运服务。

输出格式

对于输入中的每个测试用例,输出一行,包含一个整数,表示使得存在某个行程计划且 Endre\texttt{Endre}Endre 最终带 kkk 个孩子的整数 kkk 的数量。

样例

输入

2 1
1 9
1 2
4 2
1 2 3 4
1 3
1 2
4 3
30 42 105 70
2 4
1 2
2 3

输出

2
4
11

题目分析

本题的核心是:给定一个无向图,每个顶点有一个正整数权值 CiC_iCi。一条行程对应一个顶点序列(游走),Endre\texttt{Endre}Endre 会取该游走经过的所有顶点的 CCC 值的最大公约数作为可带孩子的数量 kkk。我们需要统计所有可能的游走(非空,长度任意,可重复访问顶点)所对应的最大公约数的不同取值个数。

设某次游走经过的顶点集合为 V′V'V(非空),则对应的 k=gcd⁡{Cv∣v∈V′}k = \gcd\{C_v \mid v \in V'\}k=gcd{CvvV}。由于游走可以重复访问顶点,实际上只要顶点集合在原图中是连通的(即存在一条游走恰好经过这些顶点),该集合就能被实现。因此,问题转化为:对于每个连通分量,考虑所有非空连通顶点子集,计算其顶点权值的最大公约数,求所有不同值的总数。

直接枚举所有连通子集是不可能的,因为子集数量指数级。我们需要利用权值的取值范围(Ci≤105C_i \le 10^5Ci105)以及 GCD\texttt{GCD}GCD 的性质来设计高效算法。

解题思路

核心观察

对于一个顶点集合 V′V'V,其最大公约数 ggg 必定是 V′V'V 中每个顶点权值的公约数。特别地,ggg 一定是某个 CvC_vCv 的约数。由于 Ci≤105C_i \le 10^5Ci105,每个数的约数个数最多约为 128128128 个,因此所有可能出现的 kkk 值数量是有限的。

我们考虑这样一个事实:如果从某个顶点 uuu 出发,沿任意路径到达顶点 vvv,路径上所有顶点权值的 GCD\texttt{GCD}GCD 可以通过逐步取 GCD\texttt{GCD}GCD 得到。因此,从不同起点出发,沿不同路径到达同一个顶点时,可能得到不同的 GCD\texttt{GCD}GCD 值。这些值构成了该顶点在游走中“结尾”时的可能 GCD\texttt{GCD}GCD 值集合。

更进一步,由于图是无向的,如果存在一条从 uuuvvv 的路径,其顶点集合的 GCD\texttt{GCD}GCDggg,那么反过来从 vvvuuu 也可以得到相同的 ggg(因为路径相同,顶点集合相同)。因此,我们可以通过“传播”的方式,让每个顶点维护一个集合,表示从任意起点出发,经过任意游走到达该顶点时,路径上顶点权值的 GCD\texttt{GCD}GCD 的所有可能取值。

算法设计

我们为每个顶点 uuu 维护一个集合 val[u]\textit{val}[u]val[u],存储所有可能的 GCD\texttt{GCD}GCD 值。初始时,val[u]={Cu}\textit{val}[u] = \{C_u\}val[u]={Cu},因为只访问顶点 uuu 本身的游走给出 GCD=Cu\texttt{GCD} = C_uGCD=Cu

考虑一条边 (u,v)(u, v)(u,v)。如果从某个起点到达 uuu 时得到了 GCD\texttt{GCD}GCDggg,那么从该起点继续走到 vvv 后,新的 GCD\texttt{GCD}GCDgcd⁡(g,Cv)\gcd(g, C_v)gcd(g,Cv)。因此,对于 val[u]\textit{val}[u]val[u] 中的每一个值 ggg,我们可以将 gcd⁡(g,Cv)\gcd(g, C_v)gcd(g,Cv) 插入到 val[v]\textit{val}[v]val[v] 中。由于图是无向的,反过来从 vvvuuu 也需要做同样的传播。

这类似于在图上进行“信息传播”,我们可以使用队列来加速:当一个顶点的集合插入新值时,将该顶点入队,然后处理它所有邻居,尝试用新值去更新邻居的集合。由于每个值最多被插入一次,而每个顶点的集合大小不超过 CuC_uCu 的约数个数(最多约 128128128),因此总状态数不超过 I×128I \times 128I×128。每条边被检查的次数为其两端顶点的集合大小之和,总操作量为 O(S×128)O(S \times 128)O(S×128),完全可接受。

正确性证明

  1. 充分性:如果某个值 ggg 被插入到 val[u]\textit{val}[u]val[u] 中,那么存在一个游走从某个起点到达 uuu,其经过顶点权值的 GCD\texttt{GCD}GCD 恰好为 ggg。该游走本身就是一个合法的行程,因此 ggg 是可行的。

  2. 必要性:对于任意合法的行程,其对应的顶点序列是一条游走。设该游走最终到达的顶点为 uuu,则整个游走过程中顶点的 GCD\texttt{GCD}GCD 值必然在传播过程中从起点逐步传播到 uuu,最终被插入到 val[u]\textit{val}[u]val[u] 中。因此,所有可行值都会被算法捕获。

  3. 终止性:每个顶点的集合大小有限(不超过 CuC_uCu 的约数个数),且每次插入新值才入队,因此队列处理次数有限,算法必然终止。

复杂度分析

  • 每个顶点的集合大小最多为 CuC_uCu 的约数个数,约数个数最大值在 10510^5105 内约为 128128128
  • 总插入次数 ≤I×128≤1.28×106\le I \times 128 \le 1.28 \times 10^6I×1281.28×106
  • 每条边被检查的次数为 size(val[u])+size(val[v])\textit{size}(\textit{val}[u]) + \textit{size}(\textit{val}[v])size(val[u])+size(val[v]),总和 ≤∑udeg⁡(u)×128=2S×128≤2.56×106\le \sum_{u} \deg(u) \times 128 = 2S \times 128 \le 2.56 \times 10^6udeg(u)×128=2S×1282.56×106
  • 总时间复杂度 O((I+S)×D)O((I + S) \times D)O((I+S)×D),其中 DDD 为约数个数上界,实际运行很快。
  • 空间复杂度 O(I×D)O(I \times D)O(I×D),用于存储各顶点的集合。

代码实现

// Even Distribution
// UVa ID: 12858
// Verdict: Accepted
// Submission Date: 2026-06-17
// UVa Run Time: 0.570s
//
// 版权所有(C)2026,邱秋。metaphysis # yeah dot net

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int I, S;
    while (cin >> I >> S) {
        vector<int> C(I + 1);
        for (int i = 1; i <= I; ++i) cin >> C[i];
        vector<vector<int>> adj(I + 1);
        for (int i = 0; i < S; ++i) {
            int a, b;
            cin >> a >> b;
            adj[a].push_back(b);
            adj[b].push_back(a);
        }
        // 每个顶点的set存储所有可能的GCD值
        vector<set<int>> values(I + 1);
        vector<bool> inQueue(I + 1, false);
        queue<int> q;
        for (int i = 1; i <= I; ++i) {
            values[i].insert(C[i]);
            q.push(i);
            inQueue[i] = true;
        }
        while (!q.empty()) {
            int u = q.front(); q.pop();
            inQueue[u] = false;
              for (int v : adj[u]) {
                bool updated = false;
                for (int g : values[u]) {
                    int ng = __gcd(g, C[v]);
                    if (values[v].find(ng) == values[v].end()) {
                        values[v].insert(ng);
                        updated = true;
                    }
                }
                if (updated && !inQueue[v]) {
                    q.push(v);
                    inQueue[v] = true;
                }
            }
        }
        set<int> allValues;
        for (int i = 1; i <= I; ++i)
            for (int g : values[i])
                allValues.insert(g);
        cout << allValues.size() << '\n';
    }
    return 0;
}

总结

本题的关键在于将行程问题转化为图上的连通子集 GCD\texttt{GCD}GCD 计数问题,并利用 GCD\texttt{GCD}GCD 值数量有限的特性,设计基于集合传播的算法。

关键技巧

  1. 状态压缩思想:每个顶点维护一个可能的 GCD\texttt{GCD}GCD 值集合,而不是枚举所有路径,避免了指数级复杂度。
  2. 传播模型:将路径上的 GCD\texttt{GCD}GCD 计算看作信息沿边传播的过程,使用队列优化实现高效更新。
  3. 约数数量界限:利用 Ci≤105C_i \le 10^5Ci105 这一限制,保证每个顶点的状态集合不会过大,从而控制算法复杂度。

注意事项

  • 图的连通性自动由传播过程处理,无需单独求连通分量。
  • 初始时所有顶点均入队,确保每个顶点的自身糖果数作为初始状态被传播。
  • 使用 set 存储状态天然去重,但也可改用 unordered_set 提高效率,本题数据量下 set 足够。
  • 注意多组测试用例,每次需重置数据结构。

本题综合了图论、数论和算法设计思想,是一道思维难度适中但实现细节需要仔细处理的题目。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值