BZOJ4623 : Styx

本文介绍了一种利用积性函数特性及点分治技巧解决特定问题的方法。通过分解质因数的方式,在O(nlognloglogn)的时间复杂度内计算积性函数,并运用3×3矩阵表示向量,最终实现对树状结构的有效处理。

$g$是积性函数,可以通过分解质因数在$O(n\log n \log\log n)$的时间内求出。

对于$((A\times B)\times C)\times D$,可以转化为$D\times (C\times (B\times A))$,并视向量个数的奇偶性取反答案。

对于$D\times (C\times (B\times A))$,可以将$D\times$,$C\times$,$B\times$用$3$个$3\times 3$的矩阵表示,然后对树进行点分治即可。

时间复杂度$O(n\log n \log\log n)$。

 

#include<cstdio>
const int N=100010,E=N*20,M=1000010,P=1000000007;
int n,m,i,j,x,y,a[N],mv;
int g[N],v[N<<1],nxt[N<<1],ok[N<<1],ed;
int all,f[N],son[N],now,pos[N];
int G[N],V[E],NXT[E],ED,p[N],d[N],cnt,ans[N][3];
struct Q{int x,y;}q[N];
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];ok[ed]=1;g[x]=ed;}
inline void ADD(int x,int y){V[++ED]=y;NXT[ED]=G[x];G[x]=ED;}
namespace Num{
int inv[M],p[N],tot,mul,m[M],pm[M],g[M];bool v[M];
void init(){
  for(i=2;i<=mv;i++){
    if(!v[i])p[tot++]=i;
    for(j=0;j<tot;j++){
      if(i*p[j]>mv)break;
      v[i*p[j]]=1;
      if(i%p[j]==0)break;
    }
  }
  for(inv[0]=inv[1]=1,i=2;i<=mv;i++)inv[i]=1LL*(P-inv[P%i])*(P/i)%P;
  for(mul=1,i=2;i<=mv;i++)pm[i]=g[i]=1;
}
int exgcd(int a,int b){
  if(!b)return x=1,y=0,a;
  int d=exgcd(b,a%b),t=x;
  return x=y,y=t-a/b*y,d;
}
inline int rev(int a){exgcd(a,P);while(x<0)x+=P;return x%P;}
inline void up(int&a,int b){a+=b;if(a>=P)a-=P;}
inline void add(int x){
  m[x]++;
  g[x]=(1LL*(x-1)*pm[x]%P*m[x]+g[x])%P;
  pm[x]=1LL*pm[x]*x%P;
  up(g[x],pm[x]);
}
inline void del(int x){
  up(g[x],P-pm[x]);
  pm[x]=1LL*pm[x]*inv[x]%P;
  g[x]=(1LL*(P-x+1)*pm[x]%P*m[x]+g[x])%P;
  m[x]--;
}
inline void divadd(int n){
  for(int i=0;p[i]*p[i]<=n;i++)if(n%p[i]==0){
    int j=p[i],old=rev(g[j]);
    while(n%j==0)n/=j,add(j);
    mul=1LL*mul*old%P*g[j]%P;
  }
  if(n==1)return;
  int old=rev(g[n]);
  add(n);
  mul=1LL*mul*old%P*g[n]%P;
}
inline void divdel(int n){
  for(int i=0;p[i]*p[i]<=n;i++)if(n%p[i]==0){
    int j=p[i],old=rev(g[j]);
    while(n%j==0)n/=j,del(j);
    mul=1LL*mul*old%P*g[j]%P;
  }
  if(n==1)return;
  int old=rev(g[n]);
  del(n);
  mul=1LL*mul*old%P*g[n]%P;
}
}
int mat[N][3][3],vec[N][3],A[N][3][3],B[N][3][3],C[3][3];
inline void mul(int a[][3],int b[][3],int c[][3]){
  c[0][0]=(1LL*a[0][0]*b[0][0]+1LL*a[0][1]*b[1][0]+1LL*a[0][2]*b[2][0])%P;
  c[0][1]=(1LL*a[0][0]*b[0][1]+1LL*a[0][1]*b[1][1]+1LL*a[0][2]*b[2][1])%P;
  c[0][2]=(1LL*a[0][0]*b[0][2]+1LL*a[0][1]*b[1][2]+1LL*a[0][2]*b[2][2])%P;
  c[1][0]=(1LL*a[1][0]*b[0][0]+1LL*a[1][1]*b[1][0]+1LL*a[1][2]*b[2][0])%P;
  c[1][1]=(1LL*a[1][0]*b[0][1]+1LL*a[1][1]*b[1][1]+1LL*a[1][2]*b[2][1])%P;
  c[1][2]=(1LL*a[1][0]*b[0][2]+1LL*a[1][1]*b[1][2]+1LL*a[1][2]*b[2][2])%P;
  c[2][0]=(1LL*a[2][0]*b[0][0]+1LL*a[2][1]*b[1][0]+1LL*a[2][2]*b[2][0])%P;
  c[2][1]=(1LL*a[2][0]*b[0][1]+1LL*a[2][1]*b[1][1]+1LL*a[2][2]*b[2][1])%P;
  c[2][2]=(1LL*a[2][0]*b[0][2]+1LL*a[2][1]*b[1][2]+1LL*a[2][2]*b[2][2])%P;
}
void dfs(int x,int y){
  Num::divadd(a[x]);
  vec[x][0]=Num::mul;
  vec[x][1]=x<<2;
  vec[x][2]=1;
  for(int i=g[x];i;i=nxt[i])if(v[i]!=y)dfs(v[i],x),vec[x][2]+=vec[v[i]][2];
  mat[x][0][1]=P-vec[x][2];
  mat[x][0][2]=vec[x][1];
  mat[x][1][0]=vec[x][2];
  mat[x][1][2]=P-vec[x][0];
  mat[x][2][0]=P-vec[x][1];
  mat[x][2][1]=vec[x][0];
  Num::divdel(a[x]);
}
void findroot(int x,int y){
  son[x]=1;f[x]=0;
  for(int i=g[x];i;i=nxt[i])if(ok[i]&&v[i]!=y){
    findroot(v[i],x);
    son[x]+=son[v[i]];
    if(son[v[i]]>f[x])f[x]=son[v[i]];
  }
  if(all-son[x]>f[x])f[x]=all-son[x];
  if(f[x]<f[now])now=x;
}
void dfs1(int x,int y,int z){
  pos[x]=z;f[x]=y;d[x]=d[y]^1;
  mul(mat[x],A[y],A[x]);
  for(int i=g[x];i;i=nxt[i])if(ok[i]&&v[i]!=y)dfs1(v[i],x,z);
}
void dfs2(int x,int y){
  mul(B[y],mat[x],B[x]);
  for(int i=g[x];i;i=nxt[i])if(ok[i]&&v[i]!=y)dfs2(v[i],x);
}
void solve(int x){
  if(!G[x])return;
  f[0]=all=son[x],findroot(x,now=0);
  int i;
  pos[now]=now;
  for(int i=0;i<3;i++)for(int j=0;j<3;j++)A[now][i][j]=0;
  A[now][0][0]=A[now][1][1]=A[now][2][2]=1;
  mul(A[now],mat[now],B[now]);
  f[now]=d[now]=0;
  for(i=g[now];i;i=nxt[i])if(ok[i])dfs1(v[i],now,v[i]),dfs2(v[i],now);
  for(cnt=0,i=G[x];i;i=NXT[i])p[++cnt]=V[i];G[x]=0;
  for(i=1;i<=cnt;i++)if(pos[q[p[i]].x]==pos[q[p[i]].y])ADD(pos[q[p[i]].x],p[i]);
  else{
    int X=q[p[i]].x,Y=q[p[i]].y;
    mul(A[Y],B[f[X]],C);
    ans[p[i]][0]=(1LL*C[0][0]*vec[X][0]+1LL*C[0][1]*vec[X][1]+1LL*C[0][2]*vec[X][2])%P;
    ans[p[i]][1]=(1LL*C[1][0]*vec[X][0]+1LL*C[1][1]*vec[X][1]+1LL*C[1][2]*vec[X][2])%P;
    ans[p[i]][2]=(1LL*C[2][0]*vec[X][0]+1LL*C[2][1]*vec[X][1]+1LL*C[2][2]*vec[X][2])%P;
    if(d[X]^d[Y]){
      ans[p[i]][0]=P-ans[p[i]][0];
      ans[p[i]][1]=P-ans[p[i]][1];
      ans[p[i]][2]=P-ans[p[i]][2];
    }
  }
  for(i=g[now];i;i=nxt[i])if(ok[i])ok[i^1]=0,solve(v[i]);
}
int main(){
  B[0][0][0]=B[0][1][1]=B[0][2][2]=1;
  read(n),read(m);
  for(ed=i=1;i<=n;i++){
    read(a[i]);
    if(a[i]>mv)mv=a[i];
  }
  for(i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x);
  Num::init();
  dfs(1,0);
  for(i=1;i<=m;i++){
    read(q[i].x),read(q[i].y);
    if(q[i].x==q[i].y){
      ans[i][0]=vec[q[i].x][0];
      ans[i][1]=vec[q[i].x][1];
      ans[i][2]=vec[q[i].x][2];
    }else ADD(1,i);
  }
  son[1]=n;solve(1);
  for(i=1;i<=m;i++)printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]);
  return 0;
}

  

(1)普通用户端(全平台) 音乐播放核心体验: 个性化首页:基于 “听歌历史 + 收藏偏好” 展示 “推荐歌单(每日 30 首)、新歌速递、相似曲风推荐”,支持按 “场景(通勤 / 学习 / 运动)” 切换推荐维度。 播放页功能:支持 “无损音质切换、倍速播放(0.5x-2.0x)、定时关闭、歌词逐句滚动”,提供 “沉浸式全屏模式”(隐藏冗余控件,突出歌词与专辑封面)。 多端同步:自动同步 “播放进度、收藏列表、歌单” 至所有登录设备(如手机暂停后,电脑端打开可继续播放)。 音乐发现与管理: 智能搜索:支持 “歌曲名 / 歌手 / 歌词片段” 搜索,提供 “模糊匹配(如输入‘晴天’联想‘周杰伦 - 晴天’)、热门搜索词推荐”,结果按 “热度 / 匹配度” 排序。 歌单管理:创建 “公开 / 私有 / 加密” 歌单,支持 “批量添加歌曲、拖拽排序、一键分享到社交平台”,系统自动生成 “歌单封面(基于歌曲风格配色)”。 音乐分类浏览:按 “曲风(流行 / 摇滚 / 古典)、语言(国语 / 英语 / 日语)、年代(80 后经典 / 2023 新歌)” 分层浏览,每个分类页展示 “TOP50 榜单”。 社交互动功能: 动态广场:查看 “关注的用户 / 音乐人发布的动态(如‘分享新歌感受’)、好友正在听的歌曲”,支持 “点赞 / 评论 / 转发”,可直接点击动态中的歌曲播放。 听歌排行:个人页展示 “本周听歌 TOP10、累计听歌时长”,平台定期生成 “全球 / 好友榜”(如 “好友中你本周听歌时长排名第 3”)。 音乐圈:加入 “特定曲风圈子(如‘古典音乐爱好者’)”,参与 “话题讨论(如‘你心中最经典的钢琴曲’)、线上歌单共创”。 (2)音乐人端(创作者中心) 作品管理: 音乐上传:支持 “无损音频(FLAC/WAV)+ 歌词文件(LRC)+ 专辑封面” 上传,填写 “歌曲信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值