Puzzled Elena
题目链接
题目大意
给你一棵树,n个节点n-1条边,每个节点都有一个权值。现在让你求每个节点的子树下面有多少个节点与该节点互质。
题解
容斥原理或默比乌斯反演
因为要考虑互质,而这里每个节点又是离散的,我们考虑先求出每个节点值的因子(质因子次数为1,比如8就只有一个2),然后设
d[i]
是当前子树u下因子为i的节点个数,我们可以通过容斥原理求出当前子树下与u不互质的个数,然后用子树的节点总数减去这个值就行了。
然而求
d[i]
比较难,因为这是一棵树,这里采用的方法是DFS,我们设
a[i]
是当前状态下因子为i的节点总数,如果要求某棵子树下因子为i的节点总数的话,我们DFS这个节点之前记录值,DFS后用新值减去旧的值就是当前子树下的
d[i]
。求出
d[i]
后,累加进结果就行了。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#define maxn 100005
using namespace std;
struct edge
{
int u,v,next;
};
edge e[maxn<<1];
int n,mu[maxn],p[maxn],cnt,a[maxn],tag,ans[maxn],v[maxn],nume,pre[maxn];
vector<int> fac[maxn];
bool vis[maxn];
void setup(int high)
{
cnt=0; memset(mu,0,sizeof(mu));
memset(p,0,sizeof(p));
memset(vis,0,sizeof(vis));
mu[1]=1;
for (int i=2;i<=high;i++)
{
if (!vis[i])
{
mu[i]=-1; vis[i]=1;
p[cnt++]=i;
}
for (int j=0;j<cnt&&p[j]*i<=high;j++)
{
vis[i*p[j]]=1;
if (i%p[j]) mu[i*p[j]]=-mu[i];
else
{
mu[i*p[j]]=0;
break;
}
}
}
for (int i=2;i<=high;i++) if (mu[i])
for (int j=i;j<=high;j+=i) fac[j].push_back(i);
}
void dfs(int u,int father)
{
int l1,sz;
l1=fac[v[u]].size();
vector<int> F;
sz=tag++;
for (int i=0;i<l1;i++)
{
int P=fac[ v[u] ][i];
F.push_back(a[P]);
a[P]++;
}
for (int i=pre[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if (v==father) continue;
dfs(v,u);
}
ans[u]=tag-sz;
for (int i=0;i<l1;i++)
{
int P=fac[ v[u] ][i];
int c=a[P]-F[i];
if (c) ans[u]+=c*mu[P];
}
//if (u==1) ans++;
}
void addedge(int u,int v)
{
e[nume].u=u; e[nume].v=v;
e[nume].next=pre[u];
pre[u]=nume;
nume++;
}
int main()
{
int Case=1;
setup(maxn-5);
while (scanf("%d",&n)!=EOF)
{
memset(a,0,sizeof(a));
memset(ans,0,sizeof(ans));
memset(v,0,sizeof(v));
memset(e,0,sizeof(e));
memset(pre,-1,sizeof(pre));
int c,d;
nume=0; tag=0;
for (int i=0;i<n-1;i++)
{
scanf("%d%d",&c,&d);
addedge(c,d);
addedge(d,c);
}
for (int i=1;i<=n;i++) scanf("%d",&v[i]);
dfs(1,0);
printf("Case #%d: ",Case++);
for (int i=1;i<n;i++) printf("%d ",ans[i]);
printf("%d\n",ans[n]);
}
return 0;
}