CF809E Surprise me!
Posted miracevin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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! 莫比乌斯反演虚树树形DP
One day one cf,Keep Wa away from me.