计蒜客 青云的机房组网方案

n(10w) 个结点的树。边权为 1 ,点权ai[1,10w]。求点权互质的点之间的距离之和。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define rep(x,st,en) for(int x=st;x<=en;x++)
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int M=100005;
const int Num=100000;
typedef long long ll;
int nxt[M][18],bin[20];
int dep[M];
struct Edge{
    int to,nxt;
}edge[M<<2];
//虚树中的边可以利用深度O(1)获得 
int last[M];
int head[M];
int prv,allc;
int mul[M];
int id[M],n;
ll ans,val;
void ins(int u,int v){
    edge[++allc]=(Edge){v,last[u]};
    last[u]=allc;
}
void ins_(int u,int v){
    edge[++allc]=(Edge){v,head[u]};
    head[u]=allc;
}
bool cmp(int x,int y){
    return id[x]<id[y];
}
int a[M+5];
int tot,top,dfs_clock;
int lca(int u,int v){
    if(dep[u]<dep[v])swap(u,v);
    int d=dep[u]-dep[v];
    for(int i=0;i<18;i++)
        if(bin[i]&d)
            u=nxt[u][i];
    if(u==v)return u;
    for(int i=17;i>=0;i--)
        if(nxt[u][i]!=nxt[v][i]){
            u=nxt[u][i];
            v=nxt[v][i];
        }
    return nxt[u][0];
}
int stk[M];
int sz[M];
void dfs1(int x,int f){
    for(int i=last[x];i;i=edge[i].nxt){
        int y=edge[i].to;
        if(y!=f){
            dfs1(y,x);
            sz[x]+=sz[y];
        }
    }
    if(f)val+=1ll*abs(dep[x]-dep[f])*sz[x]*(tot-sz[x]);
}
void clear(int x,int f){
    for(int i=last[x];i;i=edge[i].nxt){
        int y=edge[i].to;
        if(y!=f)clear(y,x);
    }
    last[x]=sz[x]=0;
}
void solve(int K){
    tot=top=0;
    for(int i=K;i<=Num;i+=K)
        for(int j=head[i];j;j=edge[j].nxt)
            a[tot++]=edge[j].to;
    if(tot<=1)return;
    val=0;
    allc=prv;
    sort(a,a+tot,cmp);
    for(int i=0;i<tot;i++)sz[a[i]]=1;
    for(int i=0;i<tot;i++){
        int f=0,s=a[i];
        while(top>0){
            f=lca(stk[top],s);
            if(top>1&&dep[f]<dep[stk[top-1]]){
                ins(stk[top],stk[top-1]);
                ins(stk[top-1],stk[top]);
                top--;
            }
            else if(dep[f]<dep[stk[top]]){
                ins(stk[top],f);
                ins(f,stk[top]);
                top--;
                break;
            }
            else break;
        }
        if(stk[top]!=f)stk[++top]=f;
        stk[++top]=s;
    }
    while(top>1){
        ins(stk[top],stk[top-1]);
        ins(stk[top-1],stk[top]);
        top--;
    }
    dfs1(a[0],0);
    ans+=val*mul[K];
    clear(a[0],0);
//  printf("%I64d\n",val);
}
void dfs(int x,int f){
    id[x]=++dfs_clock;
    nxt[x][0]=f;
    dep[x]=dep[f]+1;
    for(int i=0;nxt[x][i];i++)
        nxt[x][i+1]=nxt[nxt[x][i]][i];
    for(int i=last[x];i;i=edge[i].nxt){
        int y=edge[i].to;
        if(y!=f)dfs(y,x);
    }
}
char mark[M];
int prm[M];
void pre(){
    int cnt=0;
    mul[1]=1;
    for(int i=2;i<=Num;i++){
        if(!mark[i]){
            prm[cnt++]=i;
            mul[i]=-1;
        }
        for(int j=0;j<cnt&&i*prm[j]<=Num;j++){
            mark[i*prm[j]]=true;
            if(i%prm[j]==0){
                mul[i*prm[j]]=0;
                break;
            }
            else mul[i*prm[j]]=-mul[i];
        }
    }
}
int main(){
    scanf("%d",&n);
    pre();
    for(int i=0;i<20;i++)bin[i]=1<<i;

    for(int v,i=1;i<=n;i++){
        scanf("%d",&v);
        ins_(v,i);
    }
    prv=allc;
    for(int u,v,i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        ins(u,v);ins(v,u);
    }
    //这里的边得到dfs序和深度以后就用不到了 

    dfs_clock=0;
    dfs(1,0);

    ans=0;
    for(int i=1;i<=n;i++)
        last[i]=0;
    for(int i=1;i<=Num;i++)
        if(mul[i])solve(i);
    cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值