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 n
(1≤n≤105)
, after that has n−1 lines, each line has two numbers a and b (1≤a,b≤n), representing that vertex a is connect with vertex b. Then the next line has n numbers, the ith number indicates the value of the ith vertex. Values of vertices are not less than 1 and not more than 105
.
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
递归里面可以开数组,但是不能开太大,要不然会爆栈的
一棵树的每个节点的 因子是有限的, 我们不可能遍历一棵树好多次,所以我们考虑
容斥定理+前缀和(一般很多的话,差值很常见)
对于一个节点,我们知道他的子树一共有n个点,那么和 子树的根不互质的,就是有他的各个因子的数量。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#pragma comment(linker, “/STACK:1024000000,1024000000”)
#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=b-1;i>=a;--i)
const int N=1e5+10;
vector<int> vec[N];
bool vis[N];
void init(){
for(int i=2;i<N;i++){
if(vis[i])continue;
for(int j=i;j<N;j+=i){
vis[j]=1;
vec[j].push_back(i);
}
}
}
struct Edge{
int u,v,nt;
Edge(int _u=0,int _v=0,int _nt=0){
u=_u,v=_v,nt=_nt;
}
}edge[N*2];
int head[N],cnt;
void add_edge(int u,int v){
edge[cnt]=Edge(u,v,head[u]);
head[u]=cnt++;
}
int val[N];
int ans[N],sum[N],son[N];
void dfs(int u,int f){
int ttt[65],v=val[u];
int sz=vec[v].size();
int sta=1<<sz;
for(int i=0;i<sta;i++){
int tmp=1,t=0;
rep(j,0,sz){
if((1<<j)&i){//一定得移位
t++,tmp=tmp*vec[v][j];
}
}
ttt[i]=sum[tmp];
sum[tmp]++;
}
for(int i=head[u];i!=-1;i=edge[i].nt){
Edge& e=edge[i];
if(e.v==f)continue;
dfs(e.v,u);
}
ans[u]=0;
for(int i=0;i<sta;i++){
int tmp=1,t=0;
for(int j=0;j<sz;j++){
if((1<<j)&i){
t++,tmp=tmp*vec[v][j];
}
}
int sig=1;if(t&1)sig=-1;
ans[u]+=sig*(sum[tmp]-ttt[i]);
}
}
int main() {
init();
int n,kase=0;
while(scanf("%d",&n)==1){
cnt=0;
memset(head,-1,sizeof(head));
fill(ans,ans+N+1,0);
fill(sum,sum+N+1,0);
rep(i,0,n-1){
int u,v;
scanf("%d %d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
rep(i,1,n+1)scanf("%d",&val[i]);
dfs(1,-1);
printf("Case #%d: ",++kase);
rep(i,1,n+1)printf("%d%c",ans[i],i==n?'\n':' ');
}
return 0;
}
还有一种就是直接预处理在 递归体里面做的拆分(也就是莫比乌斯)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 100000;
int mu[maxn + 10], pcnt, prime[maxn];
bool vis[maxn + 10];
vector<int> factors[maxn + 10];
vector<int> G[maxn + 10];
void init() {
pcnt = 0;
mu[1] = 1;
for(int i = 2; i <= maxn; i++) {
if(!vis[i]) {
mu[i] = -1;
prime[pcnt++] = i;
}
for(int j = 0; j < pcnt && i * prime[j] <= maxn; j++) {
vis[i * prime[j]] = true;
if(i % prime[j] != 0) mu[i * prime[j]] = -mu[i];
else {
mu[i * prime[j]] = 0;
break;
}
}
}
//如果mu[i]不为0,说明他是 单个素因子的乘积
for(int i = 2; i <= maxn; i++) if(mu[i]) for(int j = i; j <= maxn; j += i) factors[j].push_back(i);
}
int val[maxn + 10];
int n;
int cnt[maxn], sz[maxn], ans[maxn];
void dfs(int u, int fa) {
sz[u] = 1;
vector<int> pre;
for(int d : factors[val[u]]) {
pre.push_back(cnt[d]);//保留下他在遍历子树之前的因子的sum值
cnt[d]++;
}
for(int v : G[u]) {
if(v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
}
ans[u] = sz[u];
for(int i = 0; i < factors[val[u]].size(); i++) {
int d = factors[val[u]][i];
int c = cnt[d] - pre[i];
if(c) ans[u] += mu[d] * c;
}
}
int main()
{
init();
int kase = 1;
while(scanf("%d", &n) == 1 && n) {
for(int i = 1; i <= n; i++) G[i].clear();
for(int u, v, i = 1; i < n; i++) {
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i = 1; i <= n; i++) scanf("%d", val + i);
memset(cnt, 0, sizeof(cnt));
dfs(1, 0);
printf("Case #%d:", kase++);
for(int i = 1; i <= n; i++) printf(" %d", ans[i]);
printf("\n");
}
return 0;
}