Codeforces 1101D - GCD Counting

传送门
虽然说这题有简单的方法,可是这其实是个点分治裸题欸

假设当前的分治到的重心为 x x x,很显然,经过 x x x的那些合法的链,肯定链中每个数都含有起码一个相同的 a [ x ] a[x] a[x]的质因子,因为 g c d ∣ a [ x ] gcd|a[x] gcda[x]嘛。把 a [ x ] a[x] a[x]分解质因数,然后对每个质因子 p p p,都做一次dfs,求出离 x x x最远的两个点 u , v u,v u,v,这两个点到x的路径上的数当然都要被 p p p整除,然后 a n s = m a x ( a n s , d i s ( u ) + d i s ( v ) ) ans=max(ans,dis(u)+dis(v)) ans=max(ans,dis(u)+dis(v)) d i s ( u ) dis(u) dis(u)表示 u u u x x x的距离。要注意 u , v u,v u,v不能在一个子树内。

然后每次找重心,不停把树分割就行了。

点分治码量比较大,但是熟练之后打起来其实还是挺快的。。大多都是板子,比赛的时候复制粘贴23333

代码丑哭。。写不来点分。。。。

#include<bits/stdc++.h>
#pragma GCC optimize("Ofast")
#define ll long long
#define fr(i,x,y) for(int i=(x);i<=(y);i++)
#define rf(i,x,y) for(int i=(x);i>=(y);i--)
#define frl(i,x,y) for(int i=(x);i<(y);i++)
using namespace std;
const int N=200002;
const int M=N<<1;
int n,a[N];
int cnt,head[N],Next[M],v[M];
int b[N];
int rt,mx,mxx;
int sz[N],mmx[N],s[M];
int ans=-1;
vector<int> p[N];

void read(int &x){
	char ch=getchar();x=0;
	for(;ch<'0'||ch>'9';ch=getchar());
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
}

void add(int x,int y){
	Next[++cnt]=head[x];
	head[x]=cnt;
	v[cnt]=y;
}

int findrt(int x,int fa,int n){ //找重心
    sz[x]=1;mmx[x]=0;
    int ans=0,tt;
    for(int i=head[x];i;i=Next[i])
     if (v[i]!=fa&&!b[v[i]]){
        tt=findrt(v[i],x,n);
        if (mmx[tt]<mmx[ans]) ans=tt;
        sz[x]+=sz[v[i]];
        mmx[x]=max(mmx[x],sz[v[i]]);
     }
    mmx[x]=max(mmx[x],n-sz[x]);
    if (mmx[x]<mmx[ans]) ans=x;
    return ans;
}

int getdis(int x,int fa,int vv){
	int mx=0;
	for(int i=head[x];i;i=Next[i])
	 if (v[i]!=fa&&!b[v[i]]&&a[v[i]]%vv==0)
	  mx=max(mx,getdis(v[i],x,vv));
	return mx+1;
}

int cal(int x,int vv){ //找最远的两个点
	ans=max(ans,0);
	int mx=-1,mxx=-1;
	for(int i=head[x];i;i=Next[i])
	 if (!b[v[i]]&&a[v[i]]%vv==0){
	 	int xx=getdis(v[i],x,vv);
	 	if (xx>mx) mx=xx;
	 	if (mx>mxx) swap(mx,mxx);
	 }
	return max(mx+mxx,mxx);
}

void dfs(int x,int n){ //分割树
	b[x]=1;int tmp=a[x];
	frl(i,0,p[tmp].size())
	 ans=max(ans,cal(x,p[tmp][i])); //对每个质因子都找最远的
	for(int i=head[x];i;i=Next[i])
	 if (!b[v[i]]){
	 	int rt=findrt(v[i],0,sz[v[i]]);
	 	assert(rt>0);
	 	dfs(rt,s[i]);
	 }
}

void init(){ //分解质因数
	frl(i,2,N)
	 if (!b[i]){
	 	p[i].push_back(i);
	 	for(int j=2*i;j<N;j+=i)
	 	 p[j].push_back(i),b[j]=1;
	 }
	memset(b,0,sizeof b);
}

int main(){
	init();
	read(n);
	fr(i,1,n) read(a[i]);
	int x,y;
	fr(i,2,n){
		read(x);read(y);
		add(x,y);add(y,x);
	}
	//int rt=findrt(1,0,n);
	mmx[0]=2e9;
	dfs(findrt(1,0,n),n);
	cout<<ans+1<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值