BZOJ 5332 [Sdoi2018]旧试题

题目链接

https://lydsy.com/JudgeOnline/problem.php?id=5332

题解

反演得到
∑ d = 1 min ⁡ ( A , B ) μ ( d ) ∑ e = 1 min ⁡ ( A , C ) μ ( e ) ∑ f = 1 min ⁡ ( B , C ) μ ( f ) F ( l c m ( d , e ) , A ) F ( l c m ( d , f ) , B ) F ( l c m ( e , f ) , C ) \sum_{d=1}^{\min(A,B)}\mu(d)\sum_{e=1}^{\min(A,C)}\mu(e)\sum_{f=1}^{\min(B,C)}\mu(f)F(\mathrm{lcm}(d,e),A)F(\mathrm{lcm}(d,f),B)F(\mathrm{lcm}(e,f),C) d=1min(A,B)μ(d)e=1min(A,C)μ(e)f=1min(B,C)μ(f)F(lcm(d,e),A)F(lcm(d,f),B)F(lcm(e,f),C)
其中
F ( a , b ) = ∑ a ∣ k ⌊ b k ⌋ F(a,b)=\sum_{a|k}\lfloor\frac{b}{k}\rfloor F(a,b)=akkb
可以对于每个 b = A , B , C b=A,B,C b=A,B,C O ( n log ⁡ n ) O(n\log n) O(nlogn)预处理出每个 a a a对应的 F F F值。

容易发现 F ( a , b ) F(a,b) F(a,b)只有在 a ≤ b a\leq b ab时才有值,因此只要两个数 x , y x,y x,y μ ( x ) = 0 \mu(x)=0 μ(x)=0 μ ( y ) = 0 \mu(y)=0 μ(y)=0 l c m ( x , y ) > max ⁡ ( A , B , C ) \mathrm{lcm}(x,y)>\max(A,B,C) lcm(x,y)>max(A,B,C),那么在枚举过程中 d , e , f d,e,f d,e,f中任意两个 = x =x =x y y y,这次枚举就一定没有贡献了。

因此可以对于每个 μ ( x ) \mu(x) μ(x) μ ( y ) \mu(y) μ(y)都不等于 0 0 0并且 l c m ( x , y ) ≤ max ⁡ ( A , B , C ) \mathrm{lcm}(x,y)\leq \max(A,B,C) lcm(x,y)max(A,B,C)时,在 x , y x,y x,y间连一条权值为 l c m ( x , y ) \mathrm{lcm}(x,y) lcm(x,y)边,那么对答案能产生贡献的就是这张图里面所有的三元环。

为了让建边不变成 O ( n 2 ) O(n^2) O(n2)的,考虑枚举边权,由于 μ ( x ) \mu(x) μ(x) μ ( y ) \mu(y) μ(y) ̸ = 0 \not= 0 ̸=0,因此边权的 μ \mu μ也不会 = 0 =0 =0,那么边权就一定可以拆成多个不同质因子之积。枚举这些质因子的子集作为 x x x,枚举 x x x的所有质因子的子集作为 gcd ⁡ ( x , y ) \gcd(x,y) gcd(x,y),容易得到 y y y,那么在 x x x y y y间连一条边即可。

容(shi)易(ji)发(shang)现,这张图其实是稀疏的,边数不会很大。

因此 O ( m m ) O(m\sqrt{m}) O(mm )的枚举三元环即可。

复杂度 O ( O( O(玄学 ) ) )。(可能)需要一些卡常技巧。

代码

#include <cstdio>
#include <vector>
#include <algorithm>
 
int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}
 
const int maxn=100000;
const int maxm=1000000;
const int mod=1000000007;
 
int p[maxn+10],prime[maxn+10],cnt,mu[maxn+10];
std::vector<int> d[maxn+10];
 
int getprime()
{
  p[1]=mu[1]=1;
  for(int i=2; i<=maxn; ++i)
    {
      if(!p[i])
        {
          prime[++cnt]=i;
          mu[i]=-1;
        }
      for(int j=1; (j<=cnt)&&(i*prime[j]<=maxn); ++j)
        {
          int x=i*prime[j];
          p[x]=1;
          if(i%prime[j]==0)
            {
              mu[x]=0;
              break;
            }
          mu[x]=-mu[i];
        }
    }
  for(int i=1; i<=cnt; ++i)
    {
      for(int j=1; j<=maxn/prime[i]; ++j)
        {
          (mu[prime[i]*j])&&(d[prime[i]*j].push_back(prime[i]),0);
        }
    }
  return 0;
}
 
int getf(int *f,int b)
{
  for(int i=1; i<=b; ++i)
    {
      for(int j=i; j<=b; j+=i)
        {
          f[i]+=b/j;
          if(f[i]>=mod)
            {
              f[i]-=mod;
            }
        }
    }
  return 0;
}
 
struct edge
{
  int x,y,v;
 
  edge(int _x=0,int _y=0,int _v=0):x(_x),y(_y),v(_v){}
};
 
int T,A,B,C,fa[maxn+10],fb[maxn+10],fc[maxn+10],deg[maxn+10],cnte,tmp[maxn+10];
edge all[maxm+10];
std::vector<edge> e[maxn+10];
 
int main()
{
  getprime();
  T=read();
  while(T--)
    {
      A=read();
      B=read();
      C=read();
      (B<C)&&(std::swap(B,C),0);
      (A<B)&&(std::swap(A,B),0);
      (B<C)&&(std::swap(B,C),0);
      for(int i=1; i<=A; ++i)
        {
          int s=d[i].size(),full=(1<<s)-1;
          for(int j=0; j<full; ++j)
            {
              int x=1;
              for(int k=0; k<s; ++k)
                {
                  (j&(1<<k))&&(x*=d[i][k]);
                }
              int oth=i/x;
              for(int k=j; k>=0; k=(k-1)&j)
                {
                  int g=1;
                  for(int l=0; l<s; ++l)
                    {
                      (k&(1<<l))&&(g*=d[i][l]);
                    }
                  int y=g*oth;
                  (x<y)&&(all[++cnte]=edge(x,y,i),0);
                  if(k==0)
                    {
                      break;
                    }
                }
            }
        }
      for(int i=1; i<=cnte; ++i)
        {
          ++deg[all[i].x];
          ++deg[all[i].y];
        }
      for(int i=1; i<=cnte; ++i)
        {
          ((deg[all[i].x]>deg[all[i].y])
           ||((deg[all[i].x]==deg[all[i].y])&&(all[i].x<all[i].y)))
            ?(e[all[i].x].push_back(edge(all[i].x,all[i].y,all[i].v)))
            :(e[all[i].y].push_back(edge(all[i].y,all[i].x,all[i].v)));
        }
      getf(fa,A);
      getf(fb,B);
      getf(fc,C);
      int ans=0;
      for(int i=1; i<=A; ++i)
        {
          for(std::vector<edge>::iterator a=e[i].begin(); a!=e[i].end(); ++a)
            {
              tmp[a->y]=a->v;
            }
          for(std::vector<edge>::iterator a=e[i].begin(); a!=e[i].end(); ++a)
            {
              for(std::vector<edge>::iterator b=e[a->y].begin(); b!=e[a->y].end(); ++b)
                {
                  (tmp[b->y])&&
                    (((ans=(ans+mu[i]*mu[a->y]*mu[b->y]
                            *(1ll*fa[a->v]*fb[b->v]*fc[tmp[b->y]]
                              +1ll*fa[a->v]*fb[tmp[b->y]]*fc[b->v]
                              +1ll*fa[b->v]*fb[a->v]*fc[tmp[b->y]]
                              +1ll*fa[b->v]*fb[tmp[b->y]]*fc[a->v]
                              +1ll*fa[tmp[b->y]]*fb[a->v]*fc[b->v]
                              +1ll*fa[tmp[b->y]]*fb[b->v]*fc[a->v]))%mod)<0)&&(ans+=mod));
                }
            }
          for(std::vector<edge>::iterator a=e[i].begin(); a!=e[i].end(); ++a)
            {
              tmp[a->y]=0;
            }
        }
      for(int i=1; i<=A; ++i)
        {
          for(std::vector<edge>::iterator a=e[i].begin(); a!=e[i].end(); ++a)
            {
              ((ans=(ans+mu[i]*(1ll*fa[a->v]*fb[a->v]*fc[a->y]
                                +1ll*fa[a->v]*fb[a->y]*fc[a->v]
                                +1ll*fa[a->y]*fb[a->v]*fc[a->v])
                     +mu[a->y]*(1ll*fa[a->v]*fb[a->v]*fc[i]
                               +1ll*fa[a->v]*fb[i]*fc[a->v]
                               +1ll*fa[i]*fb[a->v]*fc[a->v]))%mod)<0)&&(ans+=mod);
            }
        }
      for(int i=1; i<=A; ++i)
        {
          ((ans=(ans+mu[i]*1ll*fa[i]*fb[i]*fc[i])%mod)<0)&&(ans+=mod);
        }
      printf("%d\n",ans);
      for(int i=1; i<=A; ++i)
        {
          fa[i]=0;
        }
      for(int i=1; i<=B; ++i)
        {
          fb[i]=0;
        }
      for(int i=1; i<=C; ++i)
        {
          fc[i]=0;
        }
      for(int i=1; i<=A; ++i)
        {
          deg[i]=0;
        }
      for(int i=1; i<=A; ++i)
        {
          e[i].clear();
        }
      cnte=0;
    }
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值