CF809E Surprise me!

题解:

一道很套路的题目

首先一个结论

$\phi(xy)=\frac{\phi(x)*\phi(y)*gcd(x,y)}{\phi(gcd(x,y))}$

这个按照$\phi$的定义很容易知道

然后我们可以枚举gcd,很套路的可以莫比乌斯反演

然后变成给出k个点,求他们$\phi(x)*\phi(y)*dis(x,y)$

考虑所以gcd的点数为$\frac{n}{1}+\frac{n}{2}+\frac{n}{3}$=$nlogn$

于是我们需要一个与点数相关的算法

考虑虚树

之后有两种办法解决$\phi(x)*\phi(y)*dis(x,y)$

一种是考虑一个节点和它儿子节点的关系,那么就处理一下子树信息就行了

一种是把$dis(x,y)$写成$dis(x)+dis(y)-2*dis(lca(x,y))$然后dp一下 

代码:

 

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for (int i=h;i<=t;i++)
#define dep(i,t,h) for (int i=t;i>=h;i--)
#define ll long long
const int N=5e5;
const int mo=1e9+7;
int a[N],head[N],dfn[N],cnt,p[N],pos[N],q[N],bz[20][N],dep[N];
int f[N],phi[N],pri[N],prinum,ispri[N],n,l,miu[N],inv2;
bool tt[N];
struct re{
  int a,b,c;
}e[N*2];
int fsp(int x,int y)
{
  ll ans=1;
  while (y)
  {
    if (y&1) ans=ans*x%mo;
    x=1ll*x*x%mo; y>>=1; 
  }
  return ans;
}
IL void plu(int &x,int y)
{
  x=(x+y)%mo;
}
IL void arr(int x,int y)
{
  e[++l].a=head[x];
  e[l].b=y;
  head[x]=l;
}
void dfs(int x,int y)
{
  dfn[x]=++cnt; bz[0][x]=y; dep[x]=dep[y]+1;
  for (rint u=head[x];u;u=e[u].a)
  {
    int v=e[u].b;
    if (v!=y) dfs(v,x);
  }
}
bool cmp(int x,int y)
{
  return dfn[x]<dfn[y];
}
int lca(int x,int y)
{
  if (dep[x]<dep[y]) swap(x,y);
  dep(i,19,0) if (dep[bz[i][x]]>=dep[y]) x=bz[i][x];
  if (x==y) return x;
  dep(i,19,0) if (bz[i][x]!=bz[i][y]) x=bz[i][x],y=bz[i][y];
  return bz[0][x];
}
ll pp;
struct E{
  int l,head[N],ans[N];
  re e[N*2];
  IL void arr(int x,int y)
  {
    e[++l].a=head[x];
    e[l].b=y;
    e[l].c=x;
    head[x]=l;
  }
  IL void clear()
  {
    rep(i,1,l) head[e[i].c]=0;
    l=0;
  }
  void dfs(int x)
  {
    ans[x]=phi[a[x]];
    if (!tt[x])
    {
      ans[x]=0;
    }
    ll ans2=0;
    for (rint u=head[x];u;u=e[u].a)
    {
      int v=e[u].b;
      dfs(v);
      ans[x]=(ans[x]+ans[v])%mo; 
      ans2+=1ll*ans[v]*ans[v];
    }
    ans2=(1ll*ans[x]*ans[x]-ans2)%mo;
    pp=(pp+2*dep[x]*ans2)%mo;
  }
}E;
int main()
{
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  ios::sync_with_stdio(false);
  cin>>n;
  rep(i,1,n) cin>>a[i],pos[a[i]]=i;
  rep(i,1,n-1)
  {
    int x,y;
    cin>>x>>y;
    arr(x,y); arr(y,x);
  }
  dfs(1,0);
  rep(i,1,19)
    rep(j,1,n)
      bz[i][j]=bz[i-1][bz[i-1][j]];
  miu[1]=1; phi[1]=1;
  rep(i,2,n)
  {
    if (!ispri[i]) {pri[++prinum]=i; miu[i]=-1; phi[i]=i-1;}
    for (int j=1;j<=prinum&&pri[j]*i<=n;j++)
    {
      ispri[i*pri[j]]=1;
      if (i%pri[j]==0)
      {
        miu[i*pri[j]]=0;
        phi[i*pri[j]]=phi[i]*pri[j];
        break;
      } else miu[i*pri[j]]=-miu[i],phi[i*pri[j]]=phi[i]*(pri[j]-1);
    }
  }
  rep(i,1,n)
  {
    int num=0;
    int ans=0;
    ll ans2=0;
    for (int j=1;j*i<=n;j++) p[++num]=pos[i*j],plu(ans,phi[i*j]);
    rep(j,1,num) ans2=(ans2+1ll*phi[i*j]*dep[p[j]])%mo;
    ans2=2ll*ans2*ans%mo; 
    sort(p+1,p+num+1,cmp);
    rep(i,1,num) tt[p[i]]=1;
    int h=1,t=1; q[h]=1;
    rep(i,1,num)
    {
      while (h<t&&dep[lca(p[i],q[t])]<=dep[q[t-1]])
      {
        E.arr(q[t-1],q[t]); t--;
      }
      int k=lca(p[i],q[t]);
      if (k!=q[t])
      {
        E.arr(k,q[t]); q[t]=k;
      }
      if (q[t]!=p[i]) q[++t]=p[i];
    }
    while (h<t)
    {
      E.arr(q[t-1],q[t]); t--;
    }
    pp=0;
    E.dfs(1);
    ans2=((ans2-pp)%mo+mo)%mo;
    f[i]=ans2;
    E.clear();
    rep(i,1,num) tt[p[i]]=0; 
  }
  ll ans2=0;
  rep(k,1,n)
  {
     int ans=0;
     for (int j=1;j*k<=n;j++)
     {
        plu(ans,miu[j]*f[j*k]);
     }
     ans2=(ans2+1ll*k*ans%mo*fsp(phi[k],mo-2))%mo;
  }
  cout<<ans2*fsp(n,mo-2)%mo*fsp(n-1,mo-2)%mo<<endl;
  return 0;
}

 

posted @ 2019-02-08 22:49  尹吴潇  阅读(299)  评论(0编辑  收藏  举报