uoj33 [UR#2]ツリーGCD

タイトル

一般的に長いセクション+ \(\ RM \木\ ON DSU \)の考え方

尊重するために、変換を行う(私は\ [1、\を N-1] \)が決定されるどのように多くの\(F(U、V) \) を満足\(I | F(U、 V)\) 我々は最終的のような反転を行うように、

私たちはどのように多くのお尋ねになりました(F(U、V)は\ \) された\(私は\)または\(私は\)倍数、我々はすぐにクロスの長辺の時に情報をマージする必要があり、この情報は非常にシックに見えます、現在のノードの距離のような形を\(Iは\)または\(Iは\)複数のノードの数

幸いなことに、その息子軽すぎる、我々は暴力調和級数はそれを扱うことができます直接、しかしこの情報は、彼の息子が継承された場所から非常に悪いです

ルートパーティションを考えてみましょう

この場合は\(I> \ SQRT {N-} \)暴力の複雑さがされて、直接暴力重度の息子である、\(\ FRAC {N-} {私} <\ SQRT {N-} \) 私たちは自然に直接使用することができます配列の長いセクションは、いつメンテナンス取得します

しかし、\(I \当量\ SQRT { N} \) すぐにこの情報を得ることができ、メンテナンスの配列を考慮すると、そのような配列は重い息子が継承する方法はありませんときに、ラインが長いクロスパスがないかのように、それが見えます

この時間は、あなたが使用する必要があります\(\ RM DSU \)と思いました

維持考えてみましょう\(G [i]の[jは ] \) アイデアツリー内部の点の深さを表し\を(私は\)モジュロ残りはされ(j個\)\ポイント数なので、私たちが継承する方法を検討する必要はありません。彼の息子が再び空に直接十分に使用されていなかったように

我々は現在の距離にこのアレイ内部問い合わせサブツリーノードを使用することは非常に便利である\(iは\)または\(iは\)それに実際に関与現在の点の深度を使用し、複数の点の数

我々は暴力の息子を直接更新できる光とき\(G \) 更新されたすべての複雑さである\(O(\ SQRT {N })\) の

しかし、一般的に、それは、最初の長いセクションと息子の再点灯息子ですが、\(\ RM DSU \)第一の光と、次に重い息子の息子である、ここに確保するために\(グラム\)メンテナンスの情報をサブツリーから来ています内部的に、我々は好きに持っている\(\ RM DSUを\)の息子の息子の後の最初の光と重い治療に

コード

#include<bits/stdc++.h>
#define re register
#define LL long long
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
    char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=2e5+5;
struct E{int v,nxt;}e[maxn];
int n,num,__,B;
int head[maxn],son[maxn],len[maxn],dep[maxn],f[maxn],mu[maxn],p[maxn>>1];
int h[maxn],top[maxn],dfn[maxn],g[320][320],b[maxn],c[maxn];
LL ans[maxn],Ans[maxn];
inline void add(int x,int y) {
    e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
void dfs1(int x) {
    for(re int i=head[x];i;i=e[i].nxt) {
        dep[e[i].v]=dep[x]+1;dfs1(e[i].v);
        if(len[e[i].v]>len[son[x]]) son[x]=e[i].v;
    }
    len[x]=len[son[x]]+1;
}
void dfs2(int x,int topf) {
    top[x]=topf,dfn[x]=++__;
    if(!son[x]) return;
    dfs2(son[x],topf);
    for(re int i=head[x];i;i=e[i].nxt) 
    if(son[x]!=e[i].v) dfs2(e[i].v,e[i].v);
}
inline void ins(int x,int v) {
    for(re int i=1;i<=B;++i) g[i][x%i]+=v;
}
inline int calc(int x,int k) {
    if(k<=B) return g[k][dep[x]%k];
    int tot=0;
    for(re int i=dfn[x]+k;i<=dfn[x]+len[x]-1;i+=k) tot+=h[i];
    return tot;
}
void dfs(int x) {
    for(re int i=head[x];i;i=e[i].nxt) 
    if(son[x]!=e[i].v) dfs(e[i].v);
    if(son[x]) dfs(son[x]);
    for(re int i=head[x];i;i=e[i].nxt) {
        if(son[x]==e[i].v) continue;
        int y=e[i].v;
        for(re int j=1;j<=len[y];++j)
            b[j]=h[dfn[y]+j-1];
        for(re int j=1;j<=len[y];++j)
            for(re int k=j;k<=len[y];k+=j) c[j]+=b[k];
        for(re int j=1;j<=len[y];++j)
            ans[j]+=1ll*c[j]*calc(x,j);
        for(re int j=1;j<=len[y];++j) c[j]=b[j]=0;
        for(re int j=1;j<=len[y];++j)
            ins(j+dep[y]-1,h[dfn[y]+j-1]),h[dfn[x]+j]+=h[dfn[y]+j-1];
    }
    ins(dep[x],1);h[dfn[x]]++;
    if(x==top[x]) {
        for(re int i=1;i<=B;++i)
            for(re int j=dep[x];j<=dep[x]+len[x]-1;++j)
                g[i][j%i]=0;
    }
}
int main() {
    n=read();
    for(re int x,i=2;i<=n;++i) x=read(),add(x,i);
    dep[1]=1,dfs1(1),dfs2(1,1);f[1]=mu[1]=1;
    B=std::ceil(std::sqrt(n));B=min(B,310);dfs(1);
    for(re int i=2;i<=n;i++) {
        if(!f[i]) p[++p[0]]=i,mu[i]=-1;
        for(re int j=1;j<=p[0]&&p[j]*i<=n;++j) {
            f[p[j]*i]=1;if(i%p[j]==0) break;
            mu[p[j]*i]=-mu[i];
        }
    }
    for(re int i=1;i<=n;i++)
        for(re int j=i;j<=n;j+=i)
            Ans[i]+=1ll*mu[j/i]*ans[j];
    for(re int i=2;i<=n;++i) 
        c[1]++,c[dep[i]]--;
    for(re int i=1;i<=n;i++) c[i]+=c[i-1];
    for(re int i=1;i<n;i++) printf("%lld\n",Ans[i]+c[i]);
    return 0;
}

おすすめ

転載: www.cnblogs.com/asuldb/p/11487538.html