联赛模拟测试20 B. Walk (建图)

题目描述


分析

一条边只会在枚举它因子作为答案时才有用
所以,我们考虑从 \(1\) 到最大值枚举答案 \(w\),把所有倍数是 \(w\) 的边连起来
在形成的森林中跑一个直径
这样相当于把每条边分成因子个数条边
注意,你不能一开始就建好图然后在枚举时打标记,这样你走的边会变多
时间复杂度 \(O(n\times \sqrt{n})\)

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<ctime>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e6+5;
int n,h[maxn],tot=1,mmax;
struct asd{
	int to,nxt;
}b[maxn];
struct jie{
	int zb,yb;
	jie(){}
	jie(int aa,int bb){
		zb=aa,yb=bb;
	}
};
void ad(int aa,int bb){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	h[aa]=tot++;
}
std::vector<jie> g[maxn];
int ans[maxn],dis[maxn],sta[maxn],tp,vis[maxn],tim,maxdis,jl,jll;
void dfs(int now,int fa){
	vis[now]=tim;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==fa) continue;
		dis[u]=dis[now]+1;
		if(dis[u]>maxdis){
			maxdis=dis[u];
			jl=u;
		}
		dfs(u,now);
	}
}
int main(){
	freopen("walk.in","r",stdin);	
	freopen("walk.out","w",stdout);
	memset(h,-1,sizeof(h));
	n=read();
	rg int aa,bb,cc;
	for(rg int i=1;i<n;i++){
		aa=read(),bb=read(),cc=read();
		g[cc].push_back(jie(aa,bb));
		if(cc>mmax) mmax=cc;
	}
	for(rg int i=1;i<=mmax;i++){
		tp=0;
		tot=1;
		tim++;
		for(rg int j=i;j<=mmax;j+=i){
			for(rg int k=0;k<g[j].size();k++){
				sta[++tp]=g[j][k].zb;
				sta[++tp]=g[j][k].yb;
				ad(g[j][k].zb,g[j][k].yb);
				ad(g[j][k].yb,g[j][k].zb);
			}
		}
		for(rg int j=1;j<=tp;j++){
			if(vis[sta[j]]!=tim){
				maxdis=0,jl=0;
				dis[sta[j]]=0;
				dfs(sta[j],0);
				jll=jl;
				maxdis=0,jl=0;
				dis[jll]=0;
				dfs(jll,0);
				ans[maxdis]=i;
			}
		}
		for(rg int j=1;j<=tp;j++){
			h[sta[j]]=-1;
		}
	}
	for(rg int i=n;i>=1;i--){
		ans[i]=std::max(ans[i],ans[i+1]);
	}
	for(rg int i=1;i<=n;i++){
		printf("%d\n",ans[i]);
	}
	return 0;
}
posted @ 2020-10-22 10:43  liuchanglc  阅读(99)  评论(0编辑  收藏  举报