一颗大小为(n<=100000) 的树,根为1,每个节点有个权值。问:每个节点 和 以这个节点为根的子树中 有多少权值和根的权值互质。
做法,我们首先要明白一个东西,就是当你已经知道了k个数,并且求出来了每个数的素因子,如何很快的求出第k+1个数,有多少个和前k个数互质。
这个做法可以容斥做,也可以用莫比乌斯反演,莫比乌斯反演要更快些。
因为每个数的大小<=100000 这个范围内 2*3*5*7*11*13 *17> 100000 最多只有 6 个素因子。
当我们知道这个怎么处理以后,我们可以利用dfs序,解决这个问题
我们求当前这个节点的答案时,用容斥搞就是:以这个节点为根的树的大小 - 有1个素因子和他相同的节点个数 + 有2个素因子和他相同的个数 - 有3个素因子和他相同的个数 ....
那么问题来了,我们如何求出有多少个 有1个素因子和他相同的个数,有2个素因子和他相同的个数 ,,,,,
我们维护一个数a[]数组,a[i] 代表包含因子为i 的节点个数。
那么在这颗树中,进入这颗树之前求一下(有1个素因子和他相同的节点个数,有2个素因子和他相同的节点个数.....),离开这颗树的时候再求一下(有1个素因子和他相同的节点个数,有2个素因子和他相同的节点个数.....),他们的差便是我们需要的(子树中的和他有关系的信息)。
到这里,问题就解决了,容斥版的做法 时间复杂度 O(n*2^6 + nlogn) :
#include<bits/stdc++.h> using namespace std; const int maxn = 100000 + 1000; int prim[maxn],cnt; bool vis[maxn]; vector<int>F[maxn]; void get_prim() { memset(vis,0,sizeof(vis)); cnt = 0; for(int i=0;i<maxn;i++) F[i].clear(); for(int i=2;i<maxn;i++) if(!vis[i]){ prim[cnt++] = i; for(int j=i;j<maxn;j+=i){ F[j].push_back(i); vis[j] = 1; } } } int a[maxn]; int ans[maxn],val[maxn]; vector<int>G[maxn]; vector<int>factor; int fatcnt; void getFactor(int value){ factor = F[value]; fatcnt = factor.size(); } void add(int x) { getFactor(val[x]); int all = (1ll<<fatcnt); for(int i=1;i<all;i++){ int tmp = 1; for(int j=0;j<fatcnt;j++) if( i&(1ll<<j) ){ tmp *= factor[j]; } a[tmp] ++ ; } } int dfs_clock; int st[maxn],ed[maxn]; void dfs(int x,int fa) { int fuck[100]; st[x] = ++dfs_clock; getFactor(val[x]); int all = (1ll<<fatcnt); for(int i=1;i<all;i++){ int tmp = 1; for(int j=0;j<fatcnt;j++) if( i&(1ll<<j) ){ tmp *= factor[j]; } fuck[i] = a[tmp] ; } for(int i=0;i<G[x].size();i++){ int v = G[x][i]; if(v == fa) continue; dfs(v,x); } ed[x] = dfs_clock; getFactor(val[x]); all = (1ll<<fatcnt); ans[x] = ed[x] - st[x]; for(int i=1;i<all;i++){ int tmp = 1,cnt=0; for(int j=0;j<fatcnt;j++) if( i&(1ll<<j) ){ tmp *= factor[j];cnt++; } if(cnt & 1){ ans[x] -= (a[tmp] - fuck[i]); } else{ ans[x] += (a[tmp] - fuck[i]); } } add(x); if(val[x] == 1) ans[x]++; } int main() { get_prim(); int n,cas=0; while(~scanf("%d",&n)){ for(int i=1;i<=n;i++) G[i].clear(); for(int i=1;i<n;i++){ int x,y;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]); memset(a,0,sizeof(a)); dfs_clock = 0; dfs(1,-1); printf("Case #%d:",++cas); for(int i=1;i<=n;i++){ printf(" %d",ans[i]); } puts(""); } return 0; }