题目大意:

给你一棵树,树上的点编号为\(1-n\)。选两个点\(i、j\),能得到的得分是\(\phi(a_i*a_j)*dis(i,j)\),其中\(dis(i,j)\)表示\(a\)到\(b\)的最短距离。求一次选择能得到的得分的期望


推式子

显然是求\(\frac{1}{n(n-1)} \sum\limits_{i=1}^n \sum\limits_{j=1}^n \phi(i*j)*dis(i,j)\)

有这样一个式子\(\phi(i*j)=\frac{\phi(i)*phi(j)*gcd(i,j)}{\phi(gcd(i,j))}\),于是按照套路莫比乌斯反演一波

令 \(p_{a_i}=i\)

原式=\(\frac{1}{n(n-1)} \sum\limits_{d=1}^n \sum\limits_{i=1}^{n/d} \sum\limits_{j=1}^{n/d} \frac{\phi(id)*\phi(jd)*d}{\phi(d)}*dis(p_{id},p_{jd})*[gcd(i,j)==1]\)

\(=\frac{1}{n(n-1)} \sum\limits_{d=1}^n \frac{d}{\phi(d)} \sum\limits_{i=1}^{n/d}\mu(i) \sum\limits_{j=1}^{n/di} \sum\limits_{k=1}^{n/di} \phi(ijd)*\phi(ikd)*dis(p_{ijd},p_{jkd})\)

令 \(T=id\)

原式=\(\frac{1}{n(n-1)} \sum\limits_{T=1}^n \sum\limits_{d|T} \frac{d\mu(T/d)}{\phi(d)} \sum\limits_{i=1}^{n/d} \sum\limits_{j=1}^{n/d} \phi(ijd)*\phi(ikd)*dis(p_{ijd},p_{jkd})\)

\(\sum\limits_{T=1}^n \sum\limits_{d|T} \frac{d\mu(T/d)}{\phi(d)}\)可以用\(O(NlnN)\)的时间跑出来(会线性求的大佬请评论告知一下小蒟蒻做法谢谢QAQ),问题是\(\sum\limits_{i=1}^{n/d} \sum\limits_{j=1}^{n/d} \phi(ijd)*\phi(ikd)*dis(p_{ijd},p_{jkd})\),这个可以建虚树保证时间复杂度,在虚树上\(dp\)就行了……吗?


大概是个乱搞?

因为我太菜看不懂大佬们的\(dp\)做法,于是自己乱搞了一下:

设虚树上所有点的点集为\(V\),虚树上点\(x\)的贡献为\(B_x\) . 可以发现当\(w_x\neq 0\)时,\(T|a_x\)

于是就要求:

\[\sum\limits_{i\in V}\sum\limits_{j\in V} B_i*B_j*dis(i,j)\]

把距离拆开

\(\sum\limits_{i\in V}\sum\limits_{j\in V} B_i*B_j*dep_i+B_i*B_j*dep_j-2*B_i*B_j*dep_{lca}\)

令\(A_i=B_i*dep_i\),\(sum=\sum\limits _{i \in V} B_i\),则有:

\(\sum\limits_{i\in V}\sum\limits_{j\in V} A_i*w_j+A_j*B_i*dep_j-2*B_i*B_j*dep_{lca}\)

\(=2\sum\limits_{i\in V}sum*A_i-2\sum\limits_{i\in V}\sum\limits_{j\in V} B_i*B_j*dep_{lca}\)

这样就可以枚举\(lca\),\(dfs\)一遍求得答案

详见代码:

#include <bits/stdc++.h>
#define N 200010
#define mod 1000000007ll
#define ll long long
using namespace std;

int p[N],n,tot,cnt,f[N][18],dep[N],son[N],st[N],top[N],dfn[N],fa[N],fr,pr[N],bb,sz[N],d[N];
ll phi[N],S[N],u[N],inv[N],w[N],sum[N],pre,ans,B[N];
int head[N],nxt[N],v[N];
bool vis[N];
vector<int>G[N];

void init(int n){
    phi[1]=u[1]=inv[1]=1;
    for(int i=2;i<=n;++i){
        inv[i]=(1ll*mod-mod/i)*inv[mod%i]%mod;
        if(!vis[i]) pr[++cnt]=i,phi[i]=i-1,u[i]=-1;
        for(int j=1;j<=cnt && i*pr[j]<=n;++j){
            vis[i*pr[j]]=1;
            if(i%pr[j]!=0){
                u[i*pr[j]]=-u[i];
                phi[i*pr[j]]=phi[i]*(pr[j]-1);
            } else{
                u[i*pr[j]]=0;
                phi[i*pr[j]]=phi[i]*pr[j];
                break;
            }
        }
    }
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;j+=i)
            (S[j]+=1ll*u[j/i]*i%mod*inv[phi[i]]%mod)%=mod,(S[j]+=mod)%=mod;
}

void dfs1(int x,int f){
    fa[x]=f,dep[x]=dep[f]+1,sz[x]=1;
    for(int i=0;i<G[x].size();++i){
        int to=G[x][i];
        if(to!=f){
            dfs1(to,x);
            if(sz[to]>sz[son[x]]) son[x]=to;
        }
    }
}
void dfs2(int x,int s){
    dfn[x]=++cnt,top[x]=s;
    if(!son[x]) return;
    dfs2(son[x],s);
    for(int i=0;i<G[x].size();++i){
        int to=G[x][i];
        if(!dfn[to]) dfs2(to,to);
    }
}
inline int lca(int x,int y){
    if(x==0 || y==0) return 0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    if(dep[x]<dep[y]) return x;
    else return y;
}

void add(int x,int y){ v[++bb]=y,nxt[bb]=head[x],head[x]=bb; }
bool cmp(const int &x,const int &y){ return dfn[x]<dfn[y]; }

void insert(int p){
    if(fr==1){ st[++fr]=p;return; }
    int ff=lca(p,st[fr]);
    if(ff==st[fr]){
        st[++fr]=p;
        return;
    }
    while(fr>1 && dfn[st[fr-1]]>=dfn[ff]) add(st[fr-1],st[fr]),fr--;
    if(ff!=st[fr]) add(ff,st[fr]),st[fr]=ff;
    st[++fr]=p;
}
void build(int num){
    ll tmp=0;
    sort(d+1,d+num+1,cmp);
    fr=0,st[++fr]=0;
    for(int i=1;i<=num;++i){
        insert(d[i]);
        (pre+=B[d[i]])%=mod;
    }
    for(int i=1;i<=num;++i)
        (tmp+=2ll*pre%mod*B[d[i]]%mod*dep[d[i]]%mod)%=mod;
    while(fr>1)
        add(st[fr-1],st[fr]),fr--;
    ans=tmp;

}

void dfs3(int x,int f){
    sum[x]=0;
    for(int i=head[x];i;i=nxt[i]){
        int to=v[i];
        if(v[i]!=f && v[i]){
            dfs3(v[i],x);
            (sum[x]+=sum[to])%=mod;
        }
    }
    if(vis[x]) (sum[x]+=B[x])%=mod;
    for(int i=head[x];i;i=nxt[i]){
        int to=v[i];
        if(v[i]!=f && v[i]){
            (ans-=sum[to]%mod*(sum[x]-sum[to]+mod)%mod*2ll%mod*dep[x]%mod)%=mod;
            (ans+=mod)%=mod;
        }
    }
    if(vis[x]){
        (ans-=2ll*B[x]%mod*sum[x]%mod*dep[x]%mod)%=mod;
        (ans+=mod)%=mod;
    }
    head[x]=0;
}

int main(){
    int x,y,i,j;ll qwq=0;
    scanf("%d",&n);
    for(i=1;i<=n;++i) scanf("%d",&x),p[x]=i;
    init(n);
    for(i=1;i<n;++i){
        scanf("%d%d",&x,&y);
        G[x].push_back(y),G[y].push_back(x);
    }
    dfs1(1,0);
    dfs2(1,1);
    memset(vis,0,sizeof(vis));
    for(i=1;i<=n;++i){
        pre=0,tot=0;bb=0;
        for(j=i;j<=n;j+=i) d[++tot]=p[j],B[d[tot]]=phi[j],vis[d[tot]]=1;
        build(tot);
        dfs3(0,-1);
        (qwq+=S[i]*ans%mod)%=mod;
        for(j=1;j<=tot;++j) vis[d[j]]=0,B[d[j]]=0,d[j]=0;
    }
    printf("%I64d",qwq*inv[n-1]%mod*inv[n]%mod);
} 

CF809E Surprise me!(莫比乌斯反演+Dp(乱搞?))的更多相关文章

  1. CF809E Surprise me! 莫比乌斯反演、虚树

    传送门 简化题意:给出一棵\(n\)个点的树,编号为\(1\)到\(n\),第\(i\)个点的点权为\(a_i\),保证序列\(a_i\)是一个\(1\)到\(n\)的排列,求 \[ \frac{1} ...

  2. Playrix Codescapes Cup (Codeforces Round #413, rated, Div. 1 + Div. 2)(A.暴力,B.优先队列,C.dp乱搞)

    A. Carrot Cakes time limit per test:1 second memory limit per test:256 megabytes input:standard inpu ...

  3. 2016 10 28考试 dp 乱搞 树状数组

    2016 10 28 考试 时间 7:50 AM to 11:15 AM 下载链接: 试题 考试包 这次考试对自己的表现非常不满意!! T1看出来是dp题目,但是在考试过程中并没有推出转移方程,考虑了 ...

  4. BZOJ - 2500 树形DP乱搞

    题意:给出一棵树,两个给给的人在第\(i\)天会从节点\(i\)沿着最长路径走,求最长的连续天数\([L,R]\)使得\([L,R]\)为起点的最长路径极差不超过m 求\(1\)到\(n\)的最长路经 ...

  5. HZOJ 20190727 T2 单(树上dp+乱搞?+乱推式子?+dfs?)

    考试T2,考试时想到了40pts解法,即对于求b数组,随便瞎搞一下就oxxk,求a的话,很明显的高斯消元,但考试时不会打+没开double挂成10pts(我真sb),感觉考试策略还是不够成熟,而且感觉 ...

  6. Codeforces 809E Surprise me! [莫比乌斯反演]

    洛谷 Codeforces 非常套路的一道题,很适合我在陷入低谷时提升信心-- 思路 显然我们需要大力推式子. 设\(p_{a_i}=i\),则有 \[ \begin{align*} n(n-1)an ...

  7. [BZOJ4011][HNOI2015]落忆枫音-[dp乱搞+拓扑排序]

    Description 传送门 Solution 假如我们的图为DAG图,总方案数ans为每个点的入度In相乘(不算1号点).(等同于在每个点的入边选一条边,最后一定构成一棵树). 然而如果加了边x- ...

  8. [OpenJudge90][序列DP+乱搞]滑雪

    滑雪 总时间限制: 1000ms 内存限制: 65536kB [描述] Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次 ...

  9. [CSP-S模拟测试]:最小值(DP+乱搞)

    题目背景 $Maxtir$更喜欢序列的最小值. 题目传送门(内部题128) 输入格式 第一行输入一个正整数$n$和四个整数$A,B,C,D$. 第二行输入$n$个整数,第$i$个数表示$a_i$. 输 ...

随机推荐

  1. Vue之虚拟DOM

    一.真实DOM和其解析流程? 浏览器渲染引擎工作流程都差不多,大致分为5步,创建DOM树——创建StyleRules——创建Render树——布局Layout——绘制Painting 第一步,用HTM ...

  2. Greenplum扩容

    Greenplum支持原有主机扩展Segment个数.新增主机.和混合扩展 本文以在已有机器上扩展节点为例 1.可按照hostname:address:port:fselocation:dbid:co ...

  3. MongoDB基础学习

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  4. C#如何生成JSON字符串提交给接口(服务器)

    C#如何生成JSON字符串提交给接口(服务器)   第一章:C#如何拿到从http上返回JSON数据? 第二章:C#如何解析JSON数据?(反序列化对象) 第三章:C#如何生成JSON字符串?(序列化 ...

  5. 我的Spark SQL单元测试实践

    最近加入一个Spark项目,作为临时的开发人员协助进行开发工作.该项目中不存在测试的概念,开发人员按需求进行编码工作后,直接向生产系统部署,再由需求的提出者在生产系统检验程序运行结果的正确性.在这种原 ...

  6. Git 生成SSH Key

    背景:服务器是LINUX系统(centos7),使用GitLab管理git代码库.各个客户端通过sourcetree 工具,采用SSH获取.提交代码.使用SSH的方式需要公钥和私钥.下面介绍秘钥的生成 ...

  7. Offset Management For Apache Kafka With Apache Spark Streaming

    An ingest pattern that we commonly see being adopted at Cloudera customers is Apache Spark Streaming ...

  8. 10分钟,AppCan帮你搞定跨平台开发APP问题!

    跨平台开发APP时,开发者总会遇到一些问题,如打包失败等等,尤其对于iOS来说,由于它的限制性会导致一些状况发生(如证书上传问题等),小编总结了几个AppCan在线IOS打包失败常见的情况及排查技巧, ...

  9. Linux内存管理 (21)OOM

    专题:Linux内存管理专题 关键词:OOM.oom_adj.oom_score.badness. Linux内核为了提高内存的使用效率采用过度分配内存(over-commit memory)的办法, ...

  10. RuntimeError: An attempt has been made to start a new process before the current process has finished its bootstrapping phase. This probably means that you are not using fork to start your c

    Error Msg: Traceback (most recent call last): File "<string>", line 1, in <module ...