1. 程式人生 > >[LOJ]#6159. 「美團 CodeM 初賽 Round A」最長樹鏈 點分治

[LOJ]#6159. 「美團 CodeM 初賽 Round A」最長樹鏈 點分治

Description:

Mr. Walker 最近在研究樹,尤其是最長樹鏈問題。現在樹中的每個點都有一個值,他想在樹中找出最長的鏈,使得這條鏈上對應點的值的最大公約數不等於1。請求出這條最長的樹鏈的長度。

題解:

我的做法大概是最差的……不過是我自己想的。
點分治,對於每個重心找經過它的路徑答案,設 f i f_i

表示最大公約數為 i i 倍數的最大深度,由於每個數不同的質因數不會超過 10 10 個,所以每到一個點可以暴力更新它所含有的質因數的 f
f
,這樣複雜度是 O ( 10 n log n )
O(10n\log n)

正解是列舉質因數,然後把含有該質因數的點設為 1 1 後找樹的直徑,由於每個點只會在列舉到它的質因數時被訪問一次,所以複雜度為所有數的質因數個數和,十分優秀,可惜我沒有想到。
一開始TLE的懷疑人生……一度想去膜題解,還好最後發現是點分治打錯了,不要輕易放棄。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=100010;
const int inf=2147483647;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
int n,a[Maxn],ans=1;
unordered_map<int,int> f;
int gcd(int a,int b)
{
	if(!a)return b;
	return gcd(b%a,a);
}
vector<int>p[Maxn],pp[Maxn];
vector<pa>tmp;
struct Edge{int y,next;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y)
{
	int t=++len;
	e[t].y=y;e[t].next=last[x];last[x]=t;
}
int prime[4000],lp=0;bool mark[32000];
void pre()
{
	for(int i=2;i<31622;i++)
	{
		if(!mark[i])prime[++lp]=i;
		for(int j=1;prime[j]*i<31622&&j<=lp;j++)
		{
			mark[prime[j]*i]=true;
			if(i%prime[j]==0)break;
		}
	}
}
bool vis[Maxn];int root=0,son[Maxn],tot;
int get_root(int x,int fa)
{
	int sz=1,mx=0;
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa||vis[y])continue;
		int t=get_root(y,x);
		sz+=t;mx=max(mx,t);
	}
	son[x]=max(mx,tot-sz);
	if(son[x]<son[root])root=x;
	return sz;
}
int get_size(int x,int fa)
{
	int sz=1;
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa||vis[y])continue;
		sz+=get_size(y,x);
	}
	return sz;
}
void DFS(int v,int x,int fa,int op,int dep)
{
	if(op==1)
	{
		tmp.push_back(make_pair(x,dep));
		for(int i=0;i<pp[x].size();i++)
		ans=max(ans,dep+1+f[pp[x][i]]);
	}
	else
	{
		for(int i=0;i<pp[x].size();i++)
		f[pp[x][i]]=0;
	}
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa||vis[y])continue;
		int t=gcd(a[x],a[y]);
		if(t==1)continue;
		pp[y].clear();
		if(pp[x].size()<p[y].size())
		{
			for(int j=0;j<pp[x].size();j++)
			if(t%pp[x][j]==0)pp[y].push_back(pp[x][j]);
		}
		else
		{
			for(int j=0;j<p[y].size();j++)
			if(t%p[y][j]==0)pp[y].push_back(p[y][j]);
		}
		DFS(t,y,x,op,dep+1);
	}
}
void work(int x,int op)
{
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(vis[y])continue;
		int t=gcd(a[x],a[y]);
		if(t==1)continue;
		pp[y].clear();tmp.clear();
		if(p[x].size()<p[y].size())
		{
			for(int j=0;j<p[x].size();j++)
			if(t%p[x][j]==0)pp[y].push_back(p[x][j]);
		}
		else
		{
			for(int j=0;j<p[y].size();j++)
			if(t%p[y][j]==0)pp[y].push_back(p[y][j]);
		}
		DFS(t,y,x,op,1);
		for(int j=0;j<tmp.size();j++)
		for(int k=0;k<pp[tmp[j].first].size();k++)
		f[pp[tmp[j].first][k]]=max(f[pp[tmp[j].first][k]],tmp[j].second);
	}
}
void dfs(int x)
{
	vis[x]=true;
	work(x,1);work(x,-1);
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(vis[y])continue;
		tot=get_size(y,x);
		if(tot<=ans)continue;
		root=0;get_root(y,x);dfs(root);
	}
}
int main()
{
	pre();son[0]=inf;
	n=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		ins(x,y),ins(y,x);
	}
	for(int i=1;i<=n;i++)
	{
		int x=read();
		a[i]=x;
		for(int j=1;prime[j]*prime[j]<=a[i]&&j<=lp;j++)
		if(x%prime[j]==0)
		{
			p[i].push_back(prime[j]);
			while(x%prime[j]==0)x/=prime[j];
		}
		if(x!=1)p[i].push_back(x);
	}
	tot=n;get_root(1,0);dfs(root);
	printf("%d",ans);
}