Codeforces div2 843 Problem D

题目链接:Problem - D - Codeforces

解题思路:

        本题的新颖之处在于建图的方法,建好图后就是一个简单的bfs或是Dijstra求最短路的问题。因为考虑到如果直接在原图中建立边的关系不太容易,所以我们想图中加入一些素数点,这些素数是原来所有的a[i]分解后得到的素数集合,为了防止与原来的a[i]重合,我们对这些素数统一加n处理这样的话,我们还按照题中的规则建图,那么只要gcd(x,y)!=1,那么x和y一定能通过一个素数连接且素数之间互不连接,这样在原图中求得的距离再除以2就是本来的距离。

参考代码:

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
vector<int>G[2*N];
int a[N];
int n,s,t;
struct NODE{
    int id;
    int dis;
    friend bool operator <(const NODE&x,const NODE&y)
    {
        return x.dis>y.dis;
    }
};
priority_queue<NODE>pq;
int d[2*N];
int pre[2*N];
const int INF=0x3f3f3f3f;

void add(int x,int y)
{
    G[x].push_back(y);
    G[y].push_back(x);
    return;
}

void Dijstra()
{
    for(int i=0;i<2*N;i++)d[i]=INF;
    d[s]=0;
    pq.push({s,0});
    while(!pq.empty()){
        NODE u=pq.top();
        pq.pop();
        for(int i:G[u.id]){
            if(d[i]>d[u.id]+1){
                d[i]=d[u.id]+1;
                pre[i]=u.id;
                pq.push({i,d[i]});
            }
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    scanf("%d %d",&s,&t);
    //接下来是建图环节,其实也是找素因子的环节
    for(int i=1;i<=n;i++){
        //这里用到了一个小的结论即i的所有素因子中最多有一个大于根号下i的
        for(int j=2;j*j<=a[i];j++){
            //j这里可以直接加加,是因为可以确保最终与i相连的一i的一定是一个素数,如果是一个合数,那么
            //这个合数一定可以分解为若干个比它小的素数,而这些素因子再之前就已经处理掉了,矛盾
            if(a[i]%j==0){
                add(i,j+N);//建边(无向边)
            }
            while(a[i]%j==0)a[i]/=j;//这步的目的是确保消除掉a[i]中所有的j素因子
        }
        if(a[i]>1){
            add(i,a[i]+N);//这里就是可能出现的唯一一个大于根号下i的素因子
        }
    }
    //接下来是求最短路的环节
    Dijstra();
    if(d[t]==INF){
        printf("-1\n");
    }
    else{
        printf("%d\n",d[t]/2+1);
        vector<int>ans;
        for(int i=t;i!=s;i=pre[i]){
            ans.push_back(i);
        }
        ans.push_back(s);
        for(int i=ans.size()-1;i>=0;i-=2)printf("%d ",ans[i]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值