给出一颗树,每个结点都有一个权值,求出最长的一条链满足该链上所有点的最大公因数大于1。
和队友讨论了一下,队友给出了暴力建图跑树的直径思路,我感觉会tle,不过经过好几轮hack和反hack,慢慢摸索出了一种正解。
因为所要找的链的最大公因数要大于1,也就是这条链上所有结点都要有一个相同的质因子。
按照结点权值的质因子对所有结点进行分类,分类后按照质因子进行建图,对每个新建的图跑一次树的直径就是一条最长链。
例如样例,总共有 2 、 3 、 5 2、3、5 2、3、5三个质因子,第一次考虑质因子 2 2 2的时候,我们选择有质因子 2 2 2的点进行建树。剩下结点 1 、 2 、 4 1、 2、 4 1、2、4,对这三个点都跑一次树的直径(因为这个图不一定是一颗完整的树,可能是多个不连通的树。当然,我们也不必所有点跑一遍,这样必然会tle,我们可以对跑过的连通块进行标记)每次取max。
上面提到的选点的操作,事实上,我们可以将原来的树用一个vis数组标记,只有被标记的点才是我们选择的点。
t i p s : tips: tips:对于用于标记的数组,如果初始化操作要做很多次,我们可以用 v i s [ i ] = c t vis[i]=ct vis[i]=ct 标记 ,用 v i s [ i ] = = c t vis[i]==ct vis[i]==ct 判断,每一轮 c t + + ct++ ct++,这样会快很多
AC代码:
int a[maxn], vis[maxn], vis2[maxn];
int cnt = 1, head[maxn], dis[maxn], ct = 1, visct = 1;
struct node
{
int u, v, nex;
} edge[maxn << 1];
void add(int u, int v)
{
edge[cnt].v = v, edge[cnt].nex = head[u];
head[u] = cnt++;
}
map<int, vector<int>> mp;
int p1, p2;
void bfs1(int u)
{
visct++;
dis[u] = 1;
vis[u] = visct;
queue<int> q;
q.push(u);
while (!q.empty())
{
u = q.front();
q.pop();
vis[u] = visct;
for (int i = head[u]; i; i = edge[i].nex)
{
int v = edge[i].v;
if (vis[v] != visct && vis2[v] == ct)
{
dis[v] = dis[u] + 1;
p1 = v, vis[v] = visct, q.push(v);
}
}
}
}
void bfs2(int u)
{
visct++;
dis[u] = 1;
vis[u] = visct;
queue<int> q;
q.push(u);
while (!q.empty())
{
u = q.front();
q.pop();
vis[u] = visct;
for (int i = head[u]; i; i = edge[i].nex)
{
int v = edge[i].v;
if (vis[v] != visct && vis2[v] == ct)
{
dis[v] = dis[u] + 1;
p2 = v, vis[v] = visct, q.push(v);
}
}
}
}
int main()
{
int n, u, v, x;
scanf("%d", &n);
for (int i = 1; i < n; i++)
scanf("%d %d", &u, &v), add(u, v), add(v, u);
for (int i = 1; i <= n; i++)
{
scanf("%d", &x);
for (int j = 2; j * j <= x; j++) //按照质因子对点进行分类
{
bool fla = true;
while (x % j == 0)
{
x /= j;
if (fla)
{
fla = false;
mp[j].push_back(i);
}
}
}
if (x > 1)
mp[x].push_back(i);
}
int ans = 0;
for (map<int, vector<int>>::iterator pt = mp.begin(); pt != mp.end(); pt++)
{
int nowct = visct; //记录这次质因子搜索的标记起始值
int w = pt->first; //质因子w
int len = mp[w].size();
for (int i = 0; i < len; i++)
vis2[mp[w][i]] = ct; //将含有质因子w的点记为实点
for (int i = 0; i < len; i++)
{
if (vis[mp[w][i]] >= nowct && vis[mp[w][i]] <= visct) //避免同一块区域的反复搜索
continue;
bfs1(mp[w][i]);
bfs2(p1); //bfs求树的直径
ans = max(ans, dis[p2]);
}
ct++;
}
printf("%d\n", ans);
}
最长树链:质因子分治求最大公因数大于1的路径
397

被折叠的 条评论
为什么被折叠?



