题意:给一棵n个点,m条边,根节点为的树,每个节点都有有一个权值,对于每棵子树内的所有点,与根节点权值互质的点的个数。
分析:容斥原理可以求一个集合内的数与一个数互质的数的个数。而此题可以根据时间戳把一棵子树确定到一个区间内,就可以通过(1,end)-(1-begin)得到(begin,end)区间的答案。求区间互质也可以用莫比乌斯反演来做。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
vector<int> g[N];
int num[N],nu,pri[1000],ans[N],val[N];
int solve(int r,int ty)
{
int ret=0;
nu=0;
int xx=r;
for(int j=2;j*j<=xx;j++)
{
if(xx%j==0)
{
pri[nu++]=j;
while(xx%j==0)xx/=j;
}
}
if(xx!=1)pri[nu++]=xx;
int tim=1<<nu,tem,cnt;
for(int k=1;k<tim;k++)
{
tem=1;
cnt=0;
for(int j=0;j<nu;j++)
{
if(k&(1<<j))
{
tem*=pri[j];
cnt++;
}
}
if(cnt&1)
ret+=num[tem];
else
ret-=num[tem];
num[tem]+=ty;
}
return ret;
}
int dfs(int x,int fa)
{
int pre=solve(val[x],0),s=0;
for(int i=0;i<g[x].size();i++)
{
int y=g[x][i];
if(y==fa)continue;
s+=dfs(y,x);
}
int pose=solve(val[x],1);
//cout<<x<<"* "<<pre<<" "<<pose<<" "<<s<<endl;
ans[x]=s-(pose-pre);
if(val[x]==1)ans[x]++;
return s+1;
}
int main()
{
int n,x,y,cas=1;
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)g[i].clear();
memset(num,0,sizeof(num));
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
dfs(1,0);
printf("Case #%d: ",cas++);
for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' ');
}
}