HDU - 5468 Puzzled Elena dfs+容斥

Since both Stefan and Damon fell in love with Elena, and it was really difficult for her to choose. Bonnie, her best friend, suggested her to throw a question to them, and she would choose the one who can solve it. 

Suppose there is a tree with n vertices and n - 1 edges, and there is a value at each vertex. The root is vertex 1. Then for each vertex, could you tell me how many vertices of its subtree can be said to be co-prime with itself? 
NOTES: Two vertices are said to be co-prime if their values' GCD (greatest common divisor) equals 1.

Input

There are multiply tests (no more than 8). 
For each test, the first line has a number nn (1≤n≤105)(1≤n≤105), after that has n−1n−1lines, each line has two numbers a and b (1≤a,b≤n)(1≤a,b≤n), representing that vertex a is connect with vertex b. Then the next line has n numbers, the ithith number indicates the value of the ithith vertex. Values of vertices are not less than 1 and not more than 105105. 

Output

For each test, at first, please output "Case #k: ", k is the number of test. Then, please output one line with n numbers (separated by spaces), representing the answer of each vertex.

Sample Input

5
1 2
1 3
2 4
2 5
6 2 3 4 5

Sample Output

Case #1: 1 1 0 0 0

题解:

预处理,保存每个数的因子

100000 约数最多6个 2 * 3 * 5 * 7 * 11 * 13 = 30030

容斥原理求不互质的个数,sum[i]保存 累计以i为约数的个数,找到一个点时,先保存之前出现的次数,然后在dfs,互质的个数即为 孩子个数 - 不互质个数  

特!!! 该点为1的话 1与1的gcd也为1

#include<bits/stdc++.h>
using namespace std;
#define PI acos(-1)
const int N=1e5+10;
vector<int> prime[N];
vector<int> v[N];
int sum[N],ans[N],a[N],son[N];
bool ok[N];
int n;
void init()
{
	for(int i=2;i<=N-10;i++)
	{
		if(ok[i])continue;
		for(int j=i;j<=N-10;j+=i)
		{
			prime[j].push_back(i);
			ok[j]=1;
		}	
	}
}
void dfs(int u,int f)
{
	int p[65];
	int l=prime[a[u]].size();
	int len=1<<l;
	for(int i=1;i<len;i++)
	{
		int tmp=1;
		for(int j=0;j<l;j++)
			if((1<<j)&i)
				tmp*=prime[a[u]][j];
		p[i]=sum[tmp];
	}
	l=v[u].size();
	for(int i=0;i<l;i++)
	{
		if(v[u][i]!=f)
		{
			dfs(v[u][i],u);
			son[u]+=son[v[u][i]];
		}
	}
	l=prime[a[u]].size();
//	cout<<l<<endl;
	for(int i=1;i<len;i++)
	{
		int tmp=1;
		int cnt=0;
		for(int j=0;j<l;j++)
		{
			if((1<<j)&i)
			{
				tmp*=prime[a[u]][j];
				cnt++;
			}	
		}
	//	cout<<cnt<<" "<<tmp<<endl;
		if(cnt&1) ans[u]+=sum[tmp]-p[i];//容斥 奇数+ 偶数-
		else ans[u]-=sum[tmp]-p[i];
		sum[tmp]++;
	}
	ans[u]=son[u]-ans[u];
	son[u]++;
	if(a[u]==1) ans[u]+=1;
}
int main()
{
	init();
	int nn=1;
	while(~scanf("%d",&n))
	{
		for(int i=0;i<=n;i++)
			sum[i]=ans[i]=son[i]=0,v[i].clear();
		int x,y;
		for(int i=1;i<n;i++)
		{
			scanf("%d%d",&x,&y);
			v[x].push_back(y);
			v[y].push_back(x);
		}
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		dfs(1,0);
		printf("Case #%d: ",nn++);
		for(int i=1;i<=n;i++)
			printf("%d%c",ans[i]," \n"[i==n]);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值