CF809E Surprise me!

Posted miracevin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF809E Surprise me!相关的知识,希望对你有一定的参考价值。

CF809E Surprise me! 

考场上别人都会的原题就我不会

被逼无奈,还是想出了这道div1的E题!

 

phi(x*y)就是phi(x)*phi(y)*gcd(x,y)/phi(gcd(x,y))其实就是把公共质因子的(1-1/p)这些东西除掉

考虑容斥

直接枚举d,注意,这个d不是gcd,而是公共质因子乘积,所以miu(d)不能是0

配凑容斥系数,其实就是d的所有质因子子集根据大小+1-1,例如:1/x*y*z-1/x*y-1/y*z-1/x*z+1/x+1/y+1/z,这里x=(1-1/p)这样的形式

所有是d倍数的数,建成虚树,然后统计贡献

其实只要枚举每个虚树的边的贡献即可处理d(u,v)

 

写了半天:

1.O(1)LCA是快,但是注意ST判断i+(1<<j)-1<=cnt,还有lim是cnt不是n!

数组要开2倍

2.虚树还是注意mem[i]不是i

3.明确枚举的公共质因子而不是gcd!

 

代码:

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^‘0‘)
#define pb push_back
#define solid const auto &
#define enter cout<<endl
#define pii pair<int,int>
// #define int long long 
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch==-)&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+0);}
template<class T>il void ot(T x){if(x<0) putchar(-),x=-x;output(x);putchar( );}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar(\n);}
namespace Modulo{
const int mod=1e9+7;
int ad(int x,int y){return (x+y)%mod;}//>=mod?x+y-mod:x+y;}
void inc(int &x,int y){x=ad(x,y);}
int mul(int x,int y){return (ll)x*y%mod;}
void inc2(int &x,int y){x=mul(x,y);}
int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;}
}
using namespace Modulo;
namespace Miracle{
const int N=400000+5;
int n,d;
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
int val[N];
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
bool vis[N];
int pri[N],tot;
int phi[N];
int mindiv[N];
int inv[N];
int miu[N];
void sieve(int n){
    phi[1]=1;
    miu[1]=1;
    for(reg i=2;i<=n;++i){
        if(!vis[i]){
            pri[++tot]=i;
            phi[i]=i-1;
            mindiv[i]=i;
            miu[i]=-1;
        }
        for(reg j=1;j<=tot;++j){
            if(i*pri[j]>n) break;
            vis[i*pri[j]]=1;
            mindiv[i*pri[j]]=pri[j];
            if(i%pri[j]==0){
                phi[i*pri[j]]=phi[i]*pri[j];
                miu[i*pri[j]]=0;
                break;
            }
            phi[i*pri[j]]=phi[i]*(pri[j]-1);
            miu[i*pri[j]]=-miu[i];
        }
    }
}
int dfn[N],dfn2[N],dep[N];
int a[2*N],ca,lg[2*N];
int mx[2*N][19];
int fr[N];


int fa[N];
int df;
int mem[N],num;
int div[N];
ll con,ans,sum;
int sta[N],top;
bool cmp(int x,int y){
    return dfn[x]<dfn[y];
}
void dfs(int x){
    // cout<<" dfs "<<x<<" fa "<<fa[x]<<endl;
    dfn[x]=++df;
    a[++ca]=x;
    fr[x]=ca;
    dep[x]=dep[fa[x]]+1;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa[x]) continue;
        fa[y]=x;
        dfs(y);
        a[++ca]=x;
    }
    dfn2[x]=df;
}
int lca(int x,int y){
    if(x==y) return x;
    x=fr[x];y=fr[y];
    int len=y-x+1;
    int a=mx[x][lg[len]],b=mx[y-(1<<lg[len])+1][lg[len]];
    return dep[a]<dep[b]?a:b;
}
int f[N];
void dp(int x,int fa){
    // cout<<" dp "<<x<<" "<<fa<<endl;
    if(x%d==0) f[x]=phi[x];
    else f[x]=0;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        dp(y,x);
        f[x]=ad(f[x],f[y]);
    }
    if(fa){
        ans=ad(ans,mul(mul(con,mul(f[x],ad(sum,mod-f[x]))),dep[x]-dep[fa]));
    }
}
int main(){
    rd(n);
    sieve(n);
    inv[1]=1;
    for(reg i=2;i<=n;++i){
        inv[i]=mul(mod-mod/i,inv[mod%i]);
    }
    for(reg i=1;i<=n;++i){
        rd(val[i]);
    }
    int x,y;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);
        x=val[x],y=val[y];
        add(x,y);add(y,x);
    }
    dfs(1);
    for(reg i=1;i<=2*n;++i){
        lg[i]=(i>>(lg[i-1]+1)?lg[i-1]+1:lg[i-1]);
    }
    // cout<<"ca "<<ca<<endl;
    // prt(a,1,ca);
    // prt(fr,1,n);

    for(reg i=1;i<=ca;++i){
        mx[i][0]=a[i];
    }
    for(reg j=1;j<=18;++j){
        for(reg i=1;i+(1<<j)-1<=ca;++i){
            mx[i][j]=(dep[mx[i][j-1]]>dep[mx[i+(1<<(j-1))][j-1]]?mx[i+(1<<(j-1))][j-1]:mx[i][j-1]);
            // cout<<" mx i j "<<i<<" "<<j<<" : "<<mx[i][j]<<endl;
        }
    }

    cnt=0;tot=0;
    memset(hd,0,sizeof hd);
    for(d=1;d<=n;++d){
        // cout<<"--------d "<<d<<endl;
        if(!miu[d]) continue;
        con=0;tot=0;
        sum=0;
        int lp=d;
        while(lp!=1){
            int pr=mindiv[lp];
            div[tot++]=pr;
            while(mindiv[lp]==pr) lp/=pr;
        }
        for(reg s=0;s<(1<<tot);++s){
            int now=1;
            for(reg i=0;i<tot;++i){
                if(s&(1<<i)) now=mul(now,mul(div[i],inv[div[i]-1]));
            }
            // cout<<" now "<<now<<endl;
            if((tot-__builtin_popcount(s))&1){
                con=ad(con,mod-now);
            }else con=ad(con,now);
        }

        // cout<<" con "<<con<<endl;

        cnt=0;tot=0,num=0;
        for(reg j=d;j<=n;j+=d){
            mem[++num]=j;
            sum=ad(sum,phi[j]);
        }
        sort(mem+1,mem+num+1,cmp);
        int od=num;
        for(reg i=1;i<od;++i){
            int anc=lca(mem[i],mem[i+1]);
            // cout<<mem[i]<<" "<<mem[i+1]<<" anc "<<anc<<endl;
            mem[++num]=anc;
        }
        sort(mem+1,mem+num+1,cmp);
        top=0;
        mem[0]=0;
        for(reg i=1;i<=num;++i){
            if(mem[i]==mem[i-1]) continue;
            int x=mem[i];
            while(top&&!(dfn[sta[top]]<=dfn[x]&&dfn2[x]<=dfn2[sta[top]])) --top;
            if(top) {
                add(sta[top],x);
            }
            sta[++top]=x;
        }
        // cout<<" pre "<<top<<" "<<num<<endl;
        dp(mem[1],0);
        // cout<<" over "<<endl;
        //clear
        for(reg i=1;i<=num;++i){
            hd[mem[i]]=0;f[mem[i]]=0;
        }
        cnt=0;
        // cout<<" ans "<<ans<<endl;
        // memset(hd,0,sizeof hd);
    }
    // cout<<" ans "<<ans<<endl;
    ll mom=mul(n,n-1);
    ans=mul(ans,2);
    ans=mul(ans,qm(mom));
    ot(ans);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
*/

 

以上是关于CF809E Surprise me!的主要内容,如果未能解决你的问题,请参考以下文章

CF809E Surprise me!

CF809E Surprise me! 莫比乌斯反演虚树树形DP

One day one cf,Keep Wa away from me.

ModuleNotFoundError:没有名为'surprise'的模块

如何从后台弹出片段

python 安装surprise库解决 c++tools错误问题