CF1101D GCD Counting 题解
好古早的题目,模拟赛考的。
赛后想到了一个和题解都不是很像的方法(貌似交不了题解,直接在这里写吧)。(有点卡常)
想的是一个u可以到达点v,当且仅当它们有大于等于一个公共因数p。那就可以根据这个限制条件建立一个分层图,每个点内两两连通,直接求这个连通分量的直径最后再去max。
但是如果要重新建新图的话,算了一下空间有点炸。所以可以采用类似同余最短路的思想,如果两个点有相同的因数那么就可以互相到达。遍历每一个因子,跑图就可以。
感觉讲的不是很清楚,把代码加个注释吧。
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N=2e5+10 ;
vector<int> g[N],upg[N];
int dis[N],dep[N],fa[N][25],lg[N] ;
vector<int> havp[N] ;
// 权值有p这个因数的节点
bool vis[N] ; vector<int> scc ;
void getscc(int u,int p) { //找连通块
vis[u]=1 ; // 在连通块内
scc.push_back(u) ;
for(int v:g[u]) if(vis[v]==0&&dis[v]%p==0) // 如果dis[v]有p这个质因子,可以互相到达
getscc(v,p) ;
}
void dfsfar(int u,int f,int p,int depth,int &mxdep,int &res) {
// 找离u最远的far1,mxdep:scc内直径
if(depth>mxdep) {mxdep=depth ;res=u ;}
for(auto v:g[u]) {
if(v==f) continue ;
if(dis[v]%p==0) dfsfar(v,u,p,depth+1,mxdep,res) ;
}
}
int getdiam(int u,int p) {
int far1=u,mxdep=0 ;
dfsfar(u,0,p,0,mxdep,far1) ;
int far2=far1 ; mxdep=0 ;
dfsfar(far1,0,p,0,mxdep,far2) ;
return mxdep+1 ;
// 本身也算一个所以+1
}
signed main() {
// freopen("counting.in","r",stdin) ;
// freopen("counting.out","w",stdout) ;
ios::sync_with_stdio(0) ; cin.tie(0) ; cout.tie(0) ;
int n ; cin>>n ;
bool flag=1 ;
for(int i=1;i<=n;i++) { //预处理 O(n sqrt(V))
cin>>dis[i] ; int d=dis[i] ;
if(dis[i]!=1) flag=0 ;
for(int pj=2;pj*pj<=d;pj++) if(d%pj==0) { //分解质因数
havp[pj].push_back(i) ;
while(d%pj==0) d/=pj ;
}
if(d>1) havp[d].push_back(i) ;
}
for(int i=1,u,v;i<n;i++) {
cin>>u>>v ;
g[u].push_back(v) ; g[v].push_back(u) ;
}
if(flag) return cout<<0,0 ; //特判无解
int ans=0 ;
for(int p=2;p<=2e5;p++) { //找答案 O(n sqrt(V))
if(havp[p].empty()) continue ; //如果没有节点有p这个因数,就继续下一个
memset(vis,0,sizeof(vis)) ;
for(auto u:havp[p]) {
if(vis[u]||dis[u]%p!=0) continue ; //如果u已经在scc里面,就找下一个
scc.clear() ; scc.shrink_to_fit() ;
getscc(u,p) ;
int diam=getdiam(scc[0],p) ;
ans=max(ans,diam) ;
}
}
cout<<ans ;
return 0 ;
}
/*
O(n sqrt(V))
赛时构的样例:
8
2 2 2 4 5 6 6 8
1 2
2 4
4 7
4 8
2 5
5 6
1 3
*/

浙公网安备 33010602011771号