Puzzled Elena HDU - 5468 (容斥定理+前缀和)

版权声明:小白一个,欢迎各位指错。 https://blog.csdn.net/qq_36424540/article/details/82708261

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;
}

猜你喜欢

转载自blog.csdn.net/qq_36424540/article/details/82708261