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

bigbigship發表於2015-09-30

題目連結:傳送門

題意:

給定一棵樹,求這個節點的所有子樹中包括他本身與它互質的節點的個數。

分析:轉自傳送門

因為每個數的大小<=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;
}




相關文章