hdu5468
题目
给一棵树,每个结点有一个值,现在求以每个结点为根的子树中与其互质的结点的个数
思路
这位博主讲的很好http://www.cnblogs.com/program-ccc/p/5813771.html
首先我们是可以用容斥求出集合中不互素的元素个数,用总个数减一下就行了。
具体就是:①将元素进行质因数分解。②将质因数可能产生的乘积的出现次数加1。最后利用容斥原理求解。见代码。
用到的遍历是后序遍历,所以要先求出未进入该节点子节点时的不互素的数的个数,也就是与父节点其他子节点的不互素的个数,再在遍历完当前节点子节点后再求一次总数再相减就是当前节点子节点的情况。
对我来说:有了一个容斥的模版。
容斥原理在OJ中常解决两个典型问题:①求S中有多少个数与x不互素。②求1~m中有多少个数与n不互素。
代码
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=100010;
vector<int>prime[maxn];
int cnt[maxn],res[maxn];
int head[maxn],tot,n,val[maxn];
struct node
{
int next;
int to;
} edge[maxn*2];
void addedge(int from,int to)
{
edge[tot].to=to;
edge[tot].next=head[from];
head[from]=tot++;
}
void init()
{
for(int e=2; e<maxn; e++)
{
if(!prime[e].size())
for(int i=e; i<maxn; i+=e)
{
prime[i].push_back(e);
}
}
}
int cal(int n,int type)
{
int ans=0;
for(ll mark=1; mark<(1<<prime[n].size()); mark++)
{
ll odd=0;
ll mul=1;
for(ll i=0; i<prime[n].size(); i++)
{
if(mark&(1<<i))
{
odd++;
mul*=prime[n][i];
}
}
if(odd&1) ans+=cnt[mul];
else ans-=cnt[mul];
cnt[mul]+=type;
}
return ans;
}
int dfs(int u,int fa)
{
int s=0;
int pre=cal(val[u],0);
for(int i=head[u]; ~i; i=edge[i].next)
{
int v=edge[i].to;
if(v==fa) continue;
s+=dfs(v,u);
}
int post=cal(val[u],1);
res[u]=s-(post-pre);
if(val[u]==1) res[u]++;
return s+1;
}
int main()
{
init();
int kase=1;
while(scanf("%d",&n)!=EOF)
{
memset(head,-1,sizeof(head));
memset(cnt,0,sizeof(cnt));
tot=0;
for(int i=1; i<n; i++)
{
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v);
addedge(v,u);
}
for(int i=1; i<=n; i++)scanf("%d",&val[i]);
dfs(1,-1);
printf("Case #%d: ",kase++);
for(int i=1; i<n; i++)
{
printf("%d ",res[i]);
}
printf("%d\n",res[n-1]);
}
return 0;
}