D. Friendly Spiders

D. Friendly Spiders

Mars is home to an unusual species of spiders — Binary spiders.

Right now, Martian scientists are observing a colony of n spiders, the i-th of which has ai legs.

Some of the spiders are friends with each other. Namely, the i-th and j-th spiders are friends if gcd(ai,aj)1, i. e., there is some integer k2 such that ai and aj are simultaneously divided by k without a remainder. Here gcd(x,y) denotes the greatest common divisor (GCD) of integers x and y.

Scientists have discovered that spiders can send messages. If two spiders are friends, then they can transmit a message directly in one second. Otherwise, the spider must pass the message to his friend, who in turn must pass the message to his friend, and so on until the message reaches the recipient.

Let's look at an example.

Suppose a spider with eight legs wants to send a message to a spider with 15 legs. He can't do it directly, because gcd(8,15)=1. But he can send a message through the spider with six legs because gcd(8,6)=2 and gcd(6,15)=3. Thus, the message will arrive in two seconds.

Right now, scientists are observing how the s-th spider wants to send a message to the t-th spider. The researchers have a hypothesis that spiders always transmit messages optimally. For this reason, scientists would need a program that could calculate the minimum time to send a message and also deduce one of the optimal routes.

Input

The first line of input contains an integer n (2n3105) — the number of spiders in the colony.

The second line of input contains n integers a1,a2,,an (1ai3105) — the number of legs the spiders have.

The third line of input contains two integers s and t (1s,tn) —the spiders between which the message must be sent.

Output

If it is impossible to transmit a message between the given pair of spiders, print 1.

Otherwise, in the first line of the output print the integer t (t1) — the number of spiders that participate in the message transmission (i. e. the minimum time of message delivery in seconds plus one). In the second line, print t different integers b1,b2,,bt (1bin) — the ids of the spiders through which the message should follow, in order from sender to receiver.

If there are several optimal routes for the message, output any of them.

Examples

input

7
2 14 9 6 8 15 11
5 6

output

3
5 4 6 

input

7
2 14 9 6 8 15 11
5 7

output

-1

input

7
2 14 9 6 8 15 11
5 5

output

1
5

Note

The first example is shown above. It shows that the message from the 5-th spider (with eight legs) to the 6-th spider (with 15 legs) is optimal to pass through the 4-th spider (with six legs).

In the second example, the spider number 7 (with 11 legs) is not friends with anyone, so it is impossible to send him a message.

 

解题思路

  题意大概就是如果两个数的最大公约数不为1,那么就在这两个数间连一条边。如果直接按照定义来建图那么时间复杂度和空间复杂度都是O(n2),必然超时。突破口是在值域上,ai3×105,意味着我们可以尝试从值域的角度去建图。实际上官方给出的建图方法还是很难想到的(反正我是不可能想到的),看完后只能用妙来形容。

  创建一个二分图,其中左边部分由nai构成,右边部分由所有不超过3×105(或者不超过max1inai)的质数构成。遍历左边部分的每一个点,对于左边部分的某一个点ai对其进行质因数分解,在右边部分找到所有ai的质因子并各自与ai连一条边。

  下面分析时间复杂度,假设m=max1inai,建图的流程是先筛出所有不超过m的质数,这部分的时间复杂度为O(m)

  然后是分解质因数,如果直接暴力分解质因数那么时间复杂度为O(nm),最大可以达到108级别,很可能会超时。因此在筛质数的过程中顺便求出每个数的最小质因子,在分解质因数的过程中就可以根据最小质因子来试除,时间复杂度就变成了O(nlogm)

  然后试根据质因子来连边,首先因为有2×3×5×7×11×13=30030<3×105,所有在不超过3×105的数中,一个数最多有6个不同的质因子,因此建边的时间复杂度为O(26n)O(nlogm),空间复杂度为O(12n)

  最后是点数的问题,二分图左边部分有n个点,右边部分的节点个数取决于不超过m的质数的个数,因此总的节点个数最多不会超过n+m个(实际上不超过3×105的质数有25998个),其中右边部分的节点编号从n+1开始。

  建完图然后就是求最短路了,根据题意可设所有边的权值均为1,因此可以跑个bfs,时间复杂度为O(n+m+nlogm)。最后答案就是从起点到终点距离的一半,即答案要除以2(实际上就是减去所有从右边部分到左边部分的边的权值)。当然也可设从左到右的边的权值为1,从右到左的边的权值为0,然后跑个0-1bfs

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 3e5 + 10, M = 6e5 + 10, K = 36e5 + 10;
 5 
 6 int n, src, dest;
 7 int a[N];
 8 int head[M], e[K], ne[K], idx;
 9 int primes[N], minp[N], cnt;
10 bool vis[N];
11 int id[N];
12 int q[M], hh, tt = -1;
13 int dist[M], path[M];
14 
15 void add(int v, int w) {
16     e[idx] = w, ne[idx] = head[v], head[v] = idx++;
17 }
18 
19 void get_prime(int m) {
20     for (int i = 2; i <= m; i++) {
21         if (!vis[i]) {
22             primes[cnt++] = i;
23             minp[i] = i;
24             id[i] = n + cnt;    // 右边部分的节点编号从n+1开始
25         }
26         for (int j = 0; primes[j] <= m / i; j++) {
27             vis[primes[j] * i] = true;
28             minp[primes[j] * i] = primes[j];
29             if (i % primes[j] == 0) break;
30         }
31     }
32 }
33 
34 bool bfs() {
35     if (src == dest) return true;
36     q[++tt] = src;
37     memset(dist, 0x3f, sizeof(dist));
38     dist[src] = 0;
39     while (hh <= tt) {
40         int t = q[hh++];
41         for (int i = head[t]; i != -1; i = ne[i]) {
42             if (dist[e[i]] > dist[t] + 1) {
43                 dist[e[i]] = dist[t] + 1;
44                 path[e[i]] = t;
45                 if (e[i] == dest) return true;
46                 q[++tt] = e[i];
47             }
48         }
49     }
50     return false;
51 }
52 
53 int main() {
54     scanf("%d", &n);
55     for (int i = 1; i <= n; i++) {
56         scanf("%d", a + i);
57     }
58     get_prime(*max_element(a + 1, a + n + 1));    // 筛出不超过max(a[1~n])的质数
59     memset(head, -1, sizeof(head));
60     for (int i = 1; i <= n; i++) {
61         int x = a[i];
62         while (x > 1) {    // 分解质因数
63             int t = minp[x];
64             add(i, id[t]), add(id[t], i);    // 右边部分的质因子t与a[i]连一条无向边
65             while (x % t == 0) {
66                 x /= t;
67             }
68         }
69     }
70     scanf("%d %d", &src, &dest);
71     if (bfs()) {    // 有解
72         vector<int> stk;
73         while (dest) {    // 求最短路径的具体方案
74             if (dest <= n) stk.push_back(dest);    // 路径中不应该包含右边部分的点
75             dest = path[dest];
76         }
77         printf("%d\n", stk.size());
78         reverse(stk.begin(), stk.end());
79         for (auto &x : stk) {
80             printf("%d ", x);
81         }
82     }
83     else {    // 无解
84         printf("-1");
85     }
86     
87     return 0;
88 }
复制代码

  一点总结。

  如果发现有n个点可以两两互相到达,比如所有2的倍数2, 4, 6 ,如果每两个点都建一条权值为1的边的话时间复杂度和空间复杂度都是O(n2)。这题就给出了一种新的建图方式,可以开个虚点,其他点到虚点的边的权值为1,从虚点到其他点的权值为0,一样可以达到同样的效果,而时间复杂度和空间复杂度都变成了O(n),如下图:

  其中这里的虚点因为这n个点的共同关系而把这n个点连接起来,在这道题这n个点的共同关系就是都有同一个质因数x,而虚点就是二分图右边部分的质数x

  更新:参考Minimum Reverse Operations所带来的启发,这里再提供另外一种做法。

  在bfs时最重要的问题是每个节点会与大量的节点有边相连,因此在枚举所有的边时会导致超时。而根据bfs的性质,如果一个节点已经被更新了,那么到这个节点的最小距离就已经确定了,之后不会再被更新。因此在枚举相邻节点时很多节点都是无用节点(不会再被更新)。

  为此这里参考这道题目的做法,先给每一个质数开一个哈希表,用来存储含有该质因子的数所对应的下标。那么在bfs时要枚举数x能到达的相邻节点时,先对x进行质因数分解,然后对于每个不同的质因子在对应的哈希表中存在的节点进行更新,并将更新过的节点从所有哈希表中删除。

  AC代码如下,时间复杂度为O(n+nlogM)

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 3e5 + 10;
 5 
 6 int n, src, dest;
 7 int a[N];
 8 int primes[N], cnt;
 9 bool vis[N];
10 int minp[N];
11 unordered_set<int> st[N];
12 vector<int> fs[N];
13 int dist[N], path[N];
14 int q[N], hh, tt = -1;
15 
16 void get_prime(int n) {
17     for (int i = 2; i <= n; i++) {
18         if (!vis[i]) {
19             primes[cnt++] = i;
20             minp[i] = i;
21         }
22         for (int j = 0; primes[j] <= n / i; j++) {
23             vis[primes[j] * i] = true;
24             minp[primes[j] * i] = primes[j];
25             if (i % primes[j] == 0) break;
26         }
27     }
28 }
29 
30 int main() {
31     int n;
32     scanf("%d", &n);
33     for (int i = 1; i <= n; i++) {
34         scanf("%d", a + i);
35     }
36     scanf("%d %d", &src, &dest);
37     get_prime(*max_element(a + 1, a + n + 1));
38     for (int i = 1; i <= n; i++) {
39         int t = a[i];
40         while (t != 1) {
41             int p = minp[t];
42             if (i != src) st[p].insert(i);
43             fs[i].push_back(p);
44             while (t % p == 0) {
45                 t /= p;
46             }
47         }
48     }
49     memset(dist, 0x3f, sizeof(dist));
50     dist[src] = 0;
51     q[++tt] = src;
52     while (hh <= tt) {
53         int t = q[hh++];
54         for (auto &x : fs[t]) {
55             auto it = st[x].begin();
56             while (it != st[x].end()) {
57                 int c = *it;
58                 dist[c] = dist[t] + 1;
59                 path[c] = t;
60                 q[++tt] = c;
61                 auto p = next(it);
62                 for (auto &x : fs[c]) {
63                     st[x].erase(c);
64                 }
65                 it = p;
66             }
67         }
68     }
69     if (dist[dest] == 0x3f3f3f3f) {
70         printf("-1");
71     }
72     else {
73         printf("%d\n", dist[dest] + 1);
74         vector<int> ans;
75         while (dest) {
76             ans.push_back(dest);
77             dest = path[dest];
78         }
79         reverse(ans.begin(), ans.end());
80         for (auto &x : ans) {
81             printf("%d ", x);
82         }
83     }
84     
85     return 0;
86 }
复制代码

 

参考资料

  Codeforces Round #843 (Div. 2) Editorial:https://codeforces.com/blog/entry/111286

  Codeforces Round #843 (Div. 2) D(数学+图论) E(结论):https://zhuanlan.zhihu.com/p/598085405

posted @   onlyblues  阅读(119)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示