CF1553G Common Divisor Graph 题解

Description.

给定一个序列,两个元素连边了当且仅当它们的 \(\gcd>1\)
每次你可以插入一个新点,它的权值为 \(a_i\times(a_i+1)\ (i\in[1,n])\),然后对 \(n\) 加一。
多次询问,询问两个点需要几步才能联通。

Solution.

不会

考虑答案范围,只可能是 \(0,1,2\),因为你只需要对两个数分别做一次操作,就可以使它们同时向偶数连边,然后就联通了。
所以我们只需要判断 \(0\)\(1\)
\(0\) 的话很好判,每个点直接和它所有质因子合并,判断 \(s\)\(t\) 是否在同一个连通块中即可。

然后 \(1\) 的话就相当于插入一条边,我们暴力枚举插入哪条边,然后把这条边可以连接的两个点塞入一个数据结构。
查询时只需要查询两个节点所属的连通块是否存在可行的边即可。
而数据结构直接用 set 就可以了。
考虑分析复杂度,复杂度是 \(\sum_{i=1}^nd(a_i+1)\),其中 \(d(x)\) 表示 \(x\) 的因数个数。

Coding.

点击查看傻逼代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	f?x=-x:x;
}/*}}}*/
int n,Q,a[150005],fa[1000005];vector<int>v[1000005];
inline int getf(int x) {return fa[x]==x?x:fa[x]=getf(fa[x]);}
inline void mrg(int x,int y) {x=getf(x),y=getf(y);if(x^y) fa[x]=y;}
int main()
{
	read(n),read(Q);for(int i=1;i<=n;i++) read(a[i]);
	int mx=0;for(int i=1;i<=n;i++) mx=max(mx,a[i]);
	for(int i=2;i<=mx+1;i++) if(v[i].empty()) for(int j=i;j<=mx+1;j+=i) v[j].push_back(i);
	for(int i=1;i<=mx+1;i++) fa[i]=i;
	for(int i=1;i<=n;i++) for(auto j:v[a[i]]) mrg(a[i],j);
	set<pair<int,int> >st;for(int i=1;i<=n;i++)
	{
		vector<int>b=v[a[i]+1];b.push_back(a[i]);int sz=b.size();
		for(int j=0;j<sz;j++) b[j]=getf(b[j]);
		for(int j=0,x,y;j<sz;j++) for(int k=j+1;k<sz;k++)
			{x=b[j],y=b[k];if(x^y) st.insert(make_pair(min(x,y),max(x,y)));}
	}
	for(int l,r;Q--;)
	{
		read(l),read(r),l=getf(a[l]),r=getf(a[r]);
		if(l==r) {puts("0");continue;}
		puts(st.count(make_pair(min(l,r),max(l,r)))?"1":"2");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/pealfrog/p/15049860.html