标签:
题意:给定一棵树,求这个节点的所有子树中包括他本身与它互质的节点的个数.
解题思路:题利用dfs序+容斥原理+前缀和性质解决。题目中要求每个结点,和多少个它的子结点互素。如果每次为了求一个点去跑一遍dfs,复杂度将是 O(N(N+M))。一定会超时。因此需要深入加以分析。注意到n的范围是10^5以内的,因此可以事先用线性筛求出每个数含有哪些素因子。接下来,我们 尝试利用dfs序来求解。设num[i]表示遍历到当前结点时候,含有因数i(注意,不一定是素数)的结点个数。可以发现,如果第一次遍历到结点u,含有 u的因数的个数为a,那么第二次遍历到u时候,含有u的因数的个数变成了b,那么b-a就是u的子树中,含有u的因数的结点个数,即和u不互素的结点个 数。用总的结点数减掉这部分,即得到了和u互素的结点个数。这正是用了前缀和的性质。那么,如何求解有当前有多个结点含有u的因数呢?可以利用容斥原理求解。因为我们已经预处理出来了所有数的素因数,假设有len个素因数,由于“含 有”即表示只要有1个即可。因此结果就是{只含有1种素因子的个数}-{只含有2种素因子的个数}+{只含有3个素因子的个数}-...+ (-1)^(n-1){含有n个素因子的个数}。这恰好就是容斥原理。至此,问题得以解决。
1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 #include<memory.h> 7 #include<cstdlib> 8 #include<vector> 9 #define clc(a,b) memset(a,b,sizeof(a)) 10 #define LL long long int 11 using namespace std; 12 const int N=100010; 13 const double eps=1e-8; 14 const int inf=0x3f3f3f3f; 15 16 int n; 17 int val[N]; 18 int ans[N]; 19 int e; 20 int num[N]; 21 int head[N]; 22 vector<int>g[N]; 23 struct Edge 24 { 25 int to,next; 26 }edge[N*2]; 27 28 void add(int u,int v) 29 { 30 edge[e].to=v; 31 edge[e].next=head[u]; 32 head[u]=e++; 33 } 34 35 void init() 36 { 37 for(int i=2;i<N;i++) 38 { 39 if(!g[i].empty()) 40 continue; 41 for(int j=i;j<N;j+=i) 42 g[j].push_back(i); 43 } 44 } 45 46 int calc(int x,int y)//*y=0表示进入这颗树的时候,含有互质的数目为0;y=1表示dfs回溯的时候离开这棵树相应互质节点数目加一*// 47 { 48 int len=g[x].size(); 49 int res=0; 50 for(int i=1;i<(1<<len);i++) 51 { 52 int t=1; 53 int cnt=0; 54 for(int j=0;j<len;j++) 55 { 56 if(i&(1<<j)) 57 { 58 cnt++; 59 t=t*g[x][j]; 60 } 61 } 62 if(cnt%2) 63 res+=num[t]; 64 else 65 res-=num[t]; 66 num[t]+=y; 67 } 68 return res; 69 } 70 int dfs(int u,int pre) 71 { 72 int cnt=0; 73 int L=calc(val[u],0); 74 for(int i=head[u];~i;i=edge[i].next) 75 { 76 int v=edge[i].to; 77 if(v==pre) 78 continue; 79 cnt+=dfs(v,u); 80 } 81 int R=calc(val[u],1); 82 ans[u]=cnt-(R-L); 83 if(val[u]==1) 84 ans[u]++; 85 return cnt+1; 86 } 87 int main() 88 { 89 int u,v; 90 int cas=1; 91 while(~scanf("%d",&n)) 92 { 93 e=0; 94 init(); 95 clc(num,0); 96 clc(head,-1); 97 for(int i=0;i<n-1;i++) 98 { 99 scanf("%d%d",&u,&v); 100 add(u,v); 101 add(v,u); 102 } 103 for(int i=1;i<=n;i++) 104 { 105 scanf("%d",&val[i]); 106 } 107 dfs(1,0); 108 printf("Case #%d:",cas++); 109 for(int i=1;i<=n;i++) 110 { 111 printf(" %d",ans[i]); 112 } 113 printf("\n"); 114 } 115 return 0; 116 }
标签:
原文地址:http://www.cnblogs.com/ITUPC/p/4867364.html