您的位置:首页 > 其它

HDU 5468 Puzzled Elena(DFS序+容斥原理)

2015-09-30 21:23 363 查看
题目链接:传送门

题意:

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

分析:转自传送门

因为每个数的大小<=100000 这个范围内   2*3*5*7*11*13 *17> 100000 最多只有 6 个素因子。   

当我们知道这个怎么处理以后,我们可以利用dfs序,解决这个问题

我们求当前这个节点的答案时,用容斥搞就是:以这个节点为根的树的大小 - 有1个素因子和他相同的节点个数 + 有2个素因子和他相同的个数 - 有3个素因子和他相同的个数 ....

那么问题来了,我们如何求出有多少个 有1个素因子和他相同的个数,有2个素因子和他相同的个数 ,,,,,

我们维护一个数fac[]数组,fac[i] 代表包含因子i的节点个数。

那么在这颗树中,进入这颗树之前求一下(有1个素因子和他相同的节点个数,有2个素因子和他相同的节点个数.....),离开这颗树的时候再求一下(有1个素因子和他相同的节点个数,有2个素因子和他相同的节点个数.....),他们的差便是我们需要的(子树中的和他有关系的信息)。

到这里,问题就解决了,容斥版的做法 时间复杂度 O(n*2^6 + nlogn) :

代码如下:

#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e5+10;

int fac[maxn];

int w[maxn];
int ans[maxn];

struct Graph{
vector<int >vc[maxn];
void init(){
for(int i=0;i<maxn;i++)
vc[i].clear();
}
void add(int u,int v){
vc[u].push_back(v);
}
}G1,G2;

void prepare(){
G1.init();
for(int i=2;i<maxn;i++){
if(G1.vc[i].size())
continue;
for(int j=i;j<maxn;j+=i)
G1.add(j,i);
}
}

int calc(int u,int x){
int ans = 0,n=G1.vc[u].size();
for(int i=1;i<(1<<n);i++){
int tot=1,cnt=0;
for(int j=0;j<n;j++){
if((1<<j)&i){
cnt++;
tot=tot*G1.vc[u][j];
}
}
if(cnt&1)
ans = ans+fac[tot];
else
ans = ans-fac[tot];
fac[tot]+=x;
}
return ans;
}

int dfs(int u,int pre){
int cnt = 0;
int L = calc(w[u],0);
for(int i=0;i<G2.vc[u].size();i++){
int v = G2.vc[u][i];
if(v==pre) continue;
cnt+=dfs(v,u);
}
int R = calc(w[u],1);
ans[u]=cnt-(R-L);
if(w[u]==1) ans[u]++;
return cnt+1;
}

int main()
{
prepare();
int n,cas=1;
while(~scanf("%d",&n)){
G2.init();
memset(fac,0,sizeof(fac));
int u,v;
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
G2.add(u,v);
G2.add(v,u);
}
for(int i=1;i<=n;i++)
scanf("%d",w+i);
dfs(1,0);
printf("Case #%d: ",cas++);
for(int i=1;i<=n;i++){
printf("%d%c",ans[i],i==n? '\n':' ');
}
}
return 0;
}



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: