CF1916G Optimizations From Chelsu 题解

Optimizations From Chelsu

题意

给定 n 个结点的树,边有正整数边权 wi。定义 len(u,v) 表示 uv 的路径的边数,gcd(u,v) 表示 uv 的路径上所有边权的 gcd,特别地 gcd(u,u)=0。对于所有 u,v[n],求 max(len(u,v)×gcd(u,v))

大致思路

我们考虑共用一个顶点的两条链对答案的更新情况,令他们长度,gcd,分别为 len1,len2,g1,g2(len1len2),我们发现只有满足以下几个条件才能更新答案:
1. gcd(g1,g2)=g1,不然的话 gcd(g1,g2)g12
2. g2len1×g1
推导:g2=k×g1能贡献答案当且仅当 g1×(len1+len2)g1×k×len2(k1)×g1×len2g1×len1

于是我们可以考虑淀粉质,枚举重心之后,对于每个 g,求出其最长链的长度 len ,然后枚举 k(klen) ,求出 g2 更新答案即可。

复杂度证明

考虑每一层分治,对于每个 g 枚举 k 的过程看作遍历这个点到重心的路径,那发现每条边最多只会被覆盖 logw 次,,但是对 g2 的寻找我的做法会多一只 log,于是总复杂度为 O(nlogn2logw)

code

#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100005
int zu,n,k,mxl,cnt,t1,t2,t3;
int h[N],ct[N],sz[N],mxs[N],q1[N],q2[N],q3[N];
ll ans,maxn,q[N];
map<ll,int> mp,id,mp2;
struct AB{
	int a,b;
	ll c;
	int n;
}d[N*2];
void cun(int x,int y,ll z){
	d[++k]={x,y,z,h[x]},h[x]=k;
}
ll gcd(ll x,ll y){
	if(x%y==0) return y;
	return gcd(y,x%y);
}
void dfs(int x,int fa,ll g,int len,int num){
	maxn=max(maxn,g),mxl=max(mxl,len);
	if(mp.find(g)==mp.end()) mp[g]=len,id[g]=num;
	else{
		if(len>=mp[g]){
			if(num==id[g]) mp[g]=len;
			else mp2[g]=mp[g],id[g]=num,mp[g]=len;
		}
		else if(len>=mp2[g]){
			if(num!=id[g]) mp2[g]=len;
		}
	}
	for(int i=h[x];i;i=d[i].n){
		int y=d[i].b;
		if(y==fa||ct[y]) continue;
		dfs(y,x,gcd(g,d[i].c),len+1,num);
	}
}
void gt_sz(int x,int fa){
	sz[x]=1,mxs[x]=0;
	for(int i=h[x];i;i=d[i].n){
		int y=d[i].b;
		if(y==fa||ct[y]) continue;
		gt_sz(y,x);sz[x]+=sz[y],mxs[x]=max(sz[y],mxs[x]);
	}
}
int gt_rt(int x,int fa,int siz){
	int q=x,p;
	mxs[x]=max(mxs[x],siz-sz[x]);
	for(int i=h[x];i;i=d[i].n){
		int y=d[i].b;
		if(y==fa||ct[y]) continue;
		p=gt_rt(y,x,siz);
		if(mxs[p]<mxs[q]) q=p;
	}
	return q;
}
void solve(int x){
	gt_sz(x,0);
	x=gt_rt(x,0,sz[x]);
	mp.clear(),id.clear(),mp2.clear(),maxn=mxl=0;
	for(int i=h[x];i;i=d[i].n){
		int y=d[i].b;
		if(ct[y]) continue;
		dfs(y,x,d[i].c,1,y);
	}
	t1=t2=t3=0;
	for(auto p:mp) q[++t1]=p.first,q1[t1]=p.second,q3[t1]=0;
	for(auto p:id) q2[++t2]=p.second;
	for(auto p:mp2){
		while(q[t3]<p.first) t3++;
		q3[t3]=p.second;
	}
	for(int i=1;i<=t1;i++){
		ll g=q[i];
		int len=q1[i];
		if(1ll*g*(len+mxl)<=ans) continue;
		int num=q2[i];
		for(int k=1;k<=len&&1ll*g*k<=maxn;k++){
			ll g2=1ll*g*k;
			int j=lower_bound(q+1,q+1+t1,g2)-q;
			if(q[j]!=g2) continue;
			if(q2[j]==num) ans=max(ans,1ll*(q3[j]+len)*g);
			else ans=max(ans,1ll*(q1[j]+len)*g);
		}
	}
	ct[x]=1;
	for(int i=h[x];i;i=d[i].n){
		int y=d[i].b;
		if(ct[y]) continue;
		solve(y);
	}
}
inline int read(){
	int num=0;
	char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while('0'<=c&&c<='9') num=(num<<3)+(num<<1)+c-'0',c=getchar();
	return num;
}
signed main(){
	scanf("%d",&zu);
	while(zu--){
		scanf("%d",&n);
		k=0;
		for(int i=1;i<=n;i++) h[i]=ct[i]=0;
		for(int i=1,x,y;i<n;i++){
			ll z;
			x=read(),y=read(),scanf("%lld",&z);
			cun(x,y,z),cun(y,x,z);
		}
		ans=0,solve(1);
		printf("%lld\n",ans);
	}
	return 0;
}
posted @   hubingshan  阅读(86)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示