ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【2022杭电多校】第九场 1008 Shortest Path in GCD Graph 【容斥+优化】

2022-08-17 01:32:40  阅读:184  来源: 互联网

标签:杭电多校 gcd int Graph base mp cntp define GCD


链接

https://acm.hdu.edu.cn/showproblem.php?pid=7240

题意是有n个点组成的完全图,每个点的权重组成了1-n的排列,点i和点j的距离为\(gcd(i,j)\) ,给出q组询问,每次询问给出u点和v点,你需要回答u和v的最短距离和最短路的条数

思路

如果\(gcd(u,v)==1\),答案为 1 1
否则最短路的长度一定为2,因为一定可以找到一个点x(至少可以找到1)满足\(gcd(u,x)==1\), \(gcd(x,v)==1\),最短路的条数即为与u和v都互质的点数
如果\(gcd(u,v)==2\),也可以直接从u走到v,那么答案另外还要+1

由分析知,如果我们可以找到u和v的所有质因子,那么根据容斥原理,我们就可以求出1-n范围内和 u或v gcd>1的数的数量
而因为n<=1e7,u中最多有2、3、5、7、11、13、17、19,8个不同质因子,最少有23、29、31、37,4个不同质因子,所以u和v总共最多包含12个质因子

这个数据范围可以考虑枚举所有状态.具体地:

(注意必须需要使用单次询问2^12的写法,否则会tle)

做法1

枚举12个数每个数是否出现的状态,枚举出状态之后,再遍历这个状态,求出答案

如果直接枚举,时间复杂度是 12 * 2 ^ 12,会超时,因此把枚举每一位换成减去当前状态的lowbit

我这样做还是t,再存储每次询问的{u,v}二元组,如果重复问到直接回答即可ac

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define pb push_back
using namespace std;
const int N = 1e7+10, mod = 998244353;
int t, n, q, u, v, g;
int prime[22], cntp;
bool vis[N];
int cnt,p[N],log_[N];
int mp[N];
int ans;
map<pii,int>visit;

void init(int N){
    cnt=0;
    for(int i=2;i<=N;i++){
        if(!vis[i]) p[++cnt]=i;
        for(int j=1;j<=cnt&&i*p[j]<=N;j++){
            vis[i*p[j]]=1;
            if(i%p[j]==0){
                break;    
            }
        }
    }
}

void func(int x){
    if(!vis[x]){
        if(mp[x]==0){
            mp[x]=1;
            prime[cntp++]=x;
        }
        return;
    }
    for(int i=1;i<=cnt;i++){
        if(x<p[i]) break;
        if(x%p[i]==0){
            if(mp[p[i]]==0){
                mp[p[i]]=1;
                prime[cntp++]=p[i];
            }
            while(x%p[i]==0) x/=p[i];
        }
    }
    if(x>1){
        if(mp[x]==0){
            mp[x]=1;
            prime[cntp++]=x;
        }
    }
}

inline int lowbit(int x){
    return x&-x;
}

void solve(){
    ans = 0;
    for(int S = 1;S < (1 << cntp); S++){
        int sum = 0;
        ll base = 1;
        int SS = S;
        while(SS){
            int now = lowbit(SS);
            sum++;
            base *= prime[log_[now]];
            SS -= now;
        }
        if(sum & 1) ans += n/base;
        else ans -= n/base;
        // cout<<S<<' '<<base<<' '<<n/base<<endl; ///
    }
}

int main(){
    init(1e7);
    scanf("%d%d",&n,&q);
    for(int i=0;i<12;i++) log_[(1<<i)] = i;
    while(q--){
        scanf("%d%d",&u,&v);
        if(u > v) swap(u,v);
        g = __gcd(u,v);
        if(g == 1){
            puts("1 1");
            continue;
        }
        printf("2 ");
        if(visit[{u,v}]){
            printf("%d\n",visit[{u,v}]);
            continue;
        }
        cntp = 0;
        func(u); func(v);
        // cout<<cntp<<endl; 
        solve();
        ll tem = n - ans;
        if(g == 2) tem += 1;
        printf("%lld\n",tem);
        visit[{u,v}]=tem;
        for(int i=0;i<=cntp;i++) mp[prime[i]]=0;
    }
    system("pause");
    return 0;
}

做法2:dfs

直接dfs就可以做到 2 ^ 12的复杂度,因为一位一位访问的同时就存储了结果,不需要再次遍历当前状态了

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define pb push_back
using namespace std;
const int N = 1e7+10, mod = 998244353;
int t, n, q, u, v, g;
int prime[22], cntp;
bool vis[N];
int cnt,p[N],log_[N];
int mp[N];
int ans;

void init(int N){
    cnt=0;
    for(int i=2;i<=N;i++){
        if(!vis[i]) p[++cnt]=i;
        for(int j=1;j<=cnt&&i*p[j]<=N;j++){
            vis[i*p[j]]=1;
            if(i%p[j]==0){
                break;    
            }
        }
    }
}

void func(int x){
    if(!vis[x]){
        if(mp[x]==0){
            mp[x]=1;
            prime[cntp++]=x;
        }
        return;
    }
    for(int i=1;i<=cnt;i++){
        if(x<p[i]) break;
        if(x%p[i]==0){
            if(mp[p[i]]==0){
                mp[p[i]]=1;
                prime[cntp++]=p[i];
            }
            while(x%p[i]==0) x/=p[i];
        }
    }
    if(x>1){
        if(mp[x]==0){
            mp[x]=1;
            prime[cntp++]=x;
        }
    }
}

void dfs(int now,int sum,ll base){
    if(now == cntp){
        // if(base == 1) return;
        if(sum & 1) ans -= n/base;
        else ans += n/base;
        return;
    }
    dfs(now+1,sum,base);
    dfs(now+1,sum+1,base*prime[now]);
}

int main(){
    init(1e7);
    scanf("%d%d",&n,&q);
    for(int i=0;i<12;i++) log_[(1<<i)] = i;
    while(q--){
        scanf("%d%d",&u,&v);
        g = __gcd(u,v);
        if(g == 1){
            puts("1 1");
            continue;
        }
        printf("2 ");
        cntp = 0;
        func(u); func(v);
        // solve();
        ans = 0;
        dfs(0,0,1);
        // ll tem = n - ans;
        ll tem = ans;
        if(g == 2) tem += 1;
        printf("%lld\n",tem);
        for(int i=0;i<=cntp;i++) mp[prime[i]]=0;
    }
    system("pause");
    return 0;
}

标签:杭电多校,gcd,int,Graph,base,mp,cntp,define,GCD
来源: https://www.cnblogs.com/re0acm/p/16593522.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有