2015上海网络赛 A Puzzled Elena

题意:给定一棵树,求这个节点的所有子树中包括他本身与它互质的节点的个数.

解题思路:题利用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("
");
114     }
115     return 0;
116 }
View Code
原文地址:https://www.cnblogs.com/ITUPC/p/4867364.html