• [思维]Shortest Path in GCD Graph 2022杭电多校第9场 1008


    Problem Description

    There is an edge-weighted complete graph KnKn​ with nn vertices, where vertices are labeled through 1,2,...,n1,2,...,n. For each 1≤i

    You need to answer qq queries. In each query, given two vertices u,vu,v, you need to answer the length of the shortest path as well as the number of shortest paths between u,vu,v. Since the number of shortest paths may be too large, you only need to output it modulo 998244353998244353.

    Input

    The first line contains two integers n,q(2≤n≤107,1≤q≤50000)n,q(2≤n≤107,1≤q≤50000), denoting the number vertices in the graph and the number of queries, respectively.

    Then qq lines follow, where each line contains two integers u,v(1≤u,v≤n,u≠v)u,v(1≤u,v≤n,u=v), denoting a query between uu and vv.

    Output

    For each query, output one line contains two integers, denote the length and number of shortest path between given nodes, respectively. Note that only the number of shortest paths should be taken modulo 998244353998244353.

    Sample Input

    6 2

    4 5

    3 6

    Sample Output

    1 1

    2 2

    题意: 有一个由n个点构成的完全图,两点i,j之间的边权为gcd(i, j),有q次询问,每次询问给出u,v,求u到v的最短路长度和条数。

    分析: 当u和v互质时它们之间最短路一定是1,也就是直接从u走到v,条数也一定是1,当u和v不互指时最短路一定是2,因为可以从u先走到1,再从1走到v,而条数就是和u互质且和v互质的数字个数,不过当u和v的gcd本来就是2时还会多一条直接走过去的路,所以问题就转化为求n以内的和u*v互质的数字个数。先对u和v进行质因数分解,将它们出现过的质因数存下来,然后利用容斥定理可以求出与它俩不互质的数字个数,用n一减就是互质数字个数,不过这道题目时间限制比较紧,对于代码中的各种细节一定要注意优化,比如先筛出质数然后再质因数分解,容斥的时候利用lowbit快速定位1出现的位置,vector进行预分配内存等等。

    具体代码如下:

    1. #include
    2. using namespace std;
    3. const int mod = 998244353;
    4. int is_prime[10000005], prime[5000000], cnt, n, q, Log[10000005];
    5. int lowbit(int x){
    6. return x&-x;
    7. }
    8. void pre()
    9. {
    10. for(int i = 2; i <= n; i++)
    11. is_prime[i] = -1;
    12. for(int i = 0; i <= 20; i++)
    13. Log[1<
    14. for(int i = 2; i <= n; i++){
    15. if(is_prime[i])
    16. prime[++cnt] = i;
    17. for(int j = 1; j <= cnt && i*prime[j] <= n; j++){
    18. is_prime[i*prime[j]] = 0;
    19. if(i%prime[j] == 0)
    20. break;
    21. }
    22. }
    23. }
    24. signed main(){
    25. cin >> n >> q;
    26. pre();
    27. for(int i = 1; i <= q; i++){
    28. int u, v;
    29. scanf("%d%d", &u, &v);
    30. if(__gcd(u, v) == 1) puts("1 1");
    31. else{
    32. vector<int> p;
    33. p.reserve(20);
    34. unordered_map<int, bool> mp;
    35. int temp = u;
    36. for(int j = 1; prime[j]*prime[j] <= temp; j++){
    37. if(temp%prime[j] == 0){
    38. p.emplace_back(prime[j]);
    39. mp[prime[j]] = true;
    40. while(temp%prime[j] == 0)
    41. temp /= prime[j];
    42. }
    43. }
    44. if(temp != 1){
    45. p.emplace_back(temp);
    46. mp[temp] = true;
    47. }
    48. temp = v;
    49. for(int j = 1; prime[j]*prime[j] <= temp; j++){
    50. if(temp%prime[j] == 0){
    51. if(!mp[prime[j]])
    52. p.emplace_back(prime[j]);
    53. while(temp%prime[j] == 0)
    54. temp /= prime[j];
    55. }
    56. }
    57. if(temp != 1)
    58. if(!mp[temp])
    59. p.emplace_back(temp);
    60. long long num = 0;
    61. for(int j = 1; j < 1<size(); j++){
    62. int neg = 1;
    63. long long muti = 1;
    64. int temp = j;
    65. while(temp){
    66. neg ^= 1;
    67. int lb = lowbit(temp);
    68. muti *= p[Log[lb]];
    69. temp ^= lb;
    70. if(muti > n) break;
    71. }
    72. if(neg) num -= n/muti;
    73. else num += n/muti;
    74. }
    75. int ans = n-num;
    76. if(__gcd(u, v) == 2) ans++;
    77. printf("2 %d\n", ans);
    78. }
    79. }
    80. return 0;
    81. }

  • 相关阅读:
    c# 怎样能写个sql的解析器
    c++day5
    【Azure K8S】AKS升级 Kubernetes version 失败问题的分析与解决
    Android -- 每日一问:两个 Activity 之间如何传递参数?
    【集成学习】对已训练好的模型进行投票
    【Rust日报】2023-10-12 论文:利用公共信息评估 Rust 代码库
    vue封装返回顶部组件【cv可用】
    LeetCode:第304场周赛【总结】
    SQL Server2000mdf升级SQL Server2005数据库还原
    Kafka 消息队列 ( 四 ) 复杂应用
  • 原文地址:https://blog.csdn.net/m0_55982600/article/details/126373863