Harbour.Space Scholarship Contest 2021-2022 G. Common Divisor Graph

题目大意:
给你n个点与q个询问
当两个点不互质的时候连了一条边
同时你还有个操作
就是你可以选择一个点的值ai弄新的一个点an+1并且an+1=ai×(1+ai)
同时这个点也会和其他与这个点不互质的点连一条边
每个询问问两个点x,y最少需要几次操作后才能使两个点是连通的(每次询问独立)

思路:
首先我们会发现每次询问最多操作两次
因为对于点x,y
我们可以对ax进行一次操作使得一个新的点为偶数s
再对ay进行一次操作又使得一个新的点为偶数t
那么就可以通过ax->s->t->ay使得两个点连通
同时我们发现最少就是操作0次的情况也很好考虑
就是两个点本身不互质或者可以借助其他点使得这两个点连通
关键就是操作1次的情况了
这个时候因为你的操作是添加了一个点为(ax+1)ax
那其实ax+1是一个关键点
如果要操作1次的话那就是ax与(ax+1)ax不是互质的,同时ay与(ax+1)ax也不是互质的,同时就算是借助其他点的情况下ay与ax也是互质的
明白了情况之后就是想实现过程了
‘因为一直考虑是不是互质
我们可以对质数进行考虑,对于每个数开一个集合把它的质数都存进去
然后每当加入一个点的时候
把它所在的点和它的质数都弄成一个集合(或者说是并查集)并且看加入这个质数的时候看这个质数是不是已经在之前出现过了,是的话就并入这个集合
并且之后还要对每个点的值+1与这个点的值的质因子进行连边以便之后判断1的情况
具体看代码吧

AC代码:

#include
using namespace std;
const int N=1000005;
int a[N];
int fa[N+150000];
vector<int>v[N];
map<int,bool>e[N<<1];
void init(){
        for(int i=2;i<N;i++){
            if(v[i].empty())
                for(int j=i;j<N;j+=i)v[j].push_back(i);
        }
}
int findfa(int x){return x==fa[x]?x:fa[x]=findfa(fa[x]);}
int main(){
    ios::sync_with_stdio(false),cin.tie(0);
	init();
	int n,q;
	cin>>n>>q;
	for(int i=1;i<=n+N;i++)fa[i]=i;
	for(int i=1;i<=n;i++){
        cin>>a[i];
        for(auto x:v[a[i]])fa[findfa(i)]=findfa(x+n);//对于这个点和它的质因子都并入一个集合中
	}
	for(int i=1;i<=n;i++){
        for(auto x:v[a[i]])//对a[i]和a[i+1]的质因子进行连边
            for(auto y:v[a[i]+1])e[findfa(x+n)][findfa(y+n)]=1;
        for(auto x:v[a[i]+1])
            for(auto y:v[a[i]+1])e[findfa(x+n)][findfa(y+n)]=1;
	}
	while(q--){
        int x,y;
        cin>>x>>y;
        int nx=findfa(x),ny=findfa(y);//看这两个点是不是在一个集合里
        cout<<(nx==ny?0:(e[nx][ny]||e[ny][nx]?1:2))<<endl;
	}
}

你可能感兴趣的:(codeforce,题解,算法,c++,图论)