T1 GCD Counting (CF1101D)
T1 GCD Counting (CF1101D)
CF传送门
洛谷传送门
数据加强版(UOJ) 100w数据
数据加强版(洛谷) 200w数据
题意简述
给出一棵有n个节点的树,树有点权
注意一个点也可以构成一条链.
数据范围
题解
思路
首先看清题意,会发现一条链上的点权
于是乎就把难以处理的gcd转换成了简单的整除问题
从约数的角度考虑。假如我已经确定了一个
显然可以用树上dp轻松搞定
但约数个数明显也不少啊
所以不妨再退回来,一次解决所有k
再回到只观察一个点,发现它只能由与它不互质的儿子转移上来
那就只转移这些就行了
再回来考虑
如果
显然 集合A{
所以约数个数至多6个
用
用
转移时直接暴力枚举即可(因为至多也就6*6=36嘛)
注意这只是从下往上的一条路径,
所以还要边dp边更新答案
把约数个数看作常数就是
注意事项
· 建双边别忘了开两倍大小(也就只有我犯了这个错吧)
· 预处理好像可以用筛加速
但是我懒所以直接打表(最大202ms)
不加速也可以吧4.5s耶
· 别忘了初始化dp数组(全为1)
· 别忘了特判
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=20000005;
int n,a[N],f[N][7],dp[N][7],ans=1;
int k[100]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,
109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,
227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,
347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443};
//sqrt(200000)内的质数表,打表使我快乐yeah
struct nod{
int to,nxt;
}e[N*2];//双边!
int head[N],cnt;
void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void dfs(int o,int fa){
for(int i=head[o];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa) continue;
dfs(v,o);
for(int j=1;j<=f[o][0];j++)
for(int k=1;k<=f[v][0];k++)
if(f[o][j]==f[v][k])
ans=max(ans,dp[o][j]+dp[v][k]),//先更新答案
dp[o][j]=max(dp[o][j],dp[v][k]+1);//再更新dp
}
}
int main(){
scanf("%d",&n);
bool flag=1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]!=1) flag=0;//特判
}
for(int i=1;i<=n-1;i++){
int u,v; scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
if(flag) {printf("0");return 0;}//特判
for(int i=1;i<=n;i++)
for(int j=1;j<=86 && k[j]*k[j]<=a[i];j++)
if(a[i]%k[j]==0){
while(a[i]%k[j]==0) a[i]/=k[j];
f[i][++f[i][0]]=k[j];
}
//判断每个数的能否被质数整除
for(int i=1;i<=n;i++)
if(a[i]!=1)
f[i][++f[i][0]]=a[i];
//除完肯定只剩下sqrt(200000)外的质数了,直接搬下来
for(int i=1;i<=n;i++)
for(int j=1;j<=f[i][0];j++)
dp[i][j]=1;
//初始化dp数组
dfs(1,0);
printf("%d",ans);
}
时间测试