您的位置:首页 > 其它

hdu 5468 Puzzled Elena

2015-09-30 15:47 190 查看
一颗大小为(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;
}



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