luogu P3704 [SDOI2017]数字表格

传送门

我是真的弱,推式子只能推一半

下面假设\(n<m\)

考虑题目要求的东西,可以考虑每个gcd的贡献,即\[\prod_{d=1}^{n}f[d]^{\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}[gcd(i,j)=1]}\]

\(n=\sum_{d|n} \mu[d]\),得\[\prod_{d=1}^{n}f[d]^{\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{k|i,k|j}\mu[k]}\]\[\prod_{d=1}^{n}f[d]^{\sum_{k=1}^{\lfloor\frac{n}{d}\rfloor}\mu[k]\lfloor\frac{n}{kd}\rfloor\lfloor\frac{m}{kd}\rfloor}\]

大力数论分块即可获得60'好成绩

如果我们令\(t=kd\),然后把t提出来,即则\[\prod_{t=1}^{n}\prod_{d|t}f[d]^{\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}{t}\rfloor\mu[\lfloor\frac{n}{d}\rfloor]}\]\[\prod_{t=1}^{n}(\prod_{d|t}f[d]^{\mu[\lfloor\frac{n}{d}\rfloor]})^{\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}{t}\rfloor}\]

里面的可以枚举每个数倍数预处理,然后就是数论分块

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define ldb long double
#define il inline
#define re register

using namespace std;
const int N=1e6+10,mod=1e9+7;
il int rd()
{
  int x=0,w=1;char ch=0;
  while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
  while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
  return x*w;
}
il int fpow(int a,int b){int an=1;while(b){if(b&1) an=1ll*an*a%mod;a=1ll*a*a%mod,b>>=1;}return an;}
bool v[N];
int prm[N],mu[N],tt,fb[N],ifb[N],f[N],ivf[N];

int main()
{
  fb[0]=0,fb[1]=1,ifb[0]=ifb[1]=1;
  for(int i=2;i<=N-10;++i) fb[i]=(fb[i-1]+fb[i-2])%mod,ifb[i]=fpow(fb[i],mod-2);
  mu[1]=1;
  for(int i=2;i<=N-10;++i)
    {
      if(!v[i]) v[i]=1,prm[++tt]=i,mu[i]=-1;
      for(int j=1;j<=tt&&i*prm[j]<=N-10;++j)
        {
          v[i*prm[j]]=1,mu[i*prm[j]]=-mu[i];
          if(i%prm[j]==0) {mu[i*prm[j]]=0;break;}
        }
    }
  for(int i=0;i<=N-10;++i) f[i]=1;
  for(int i=1;i<=N-10;++i)
    for(int j=1;i*j<=N-10;++j)
      f[i*j]=1ll*f[i*j]*((~mu[j])?fpow(fb[i],mu[j]):ifb[i])%mod;
  ivf[0]=1;
  for(int i=1;i<=N-10;++i) f[i]=1ll*f[i]*f[i-1]%mod,ivf[i]=fpow(f[i],mod-2);
  int T=rd();
  while(T--)
    {
      int n=rd(),m=rd(),ans=1;
      if(n>m) swap(n,m);
      for(int i=1,j;i<=n;i=j+1)
        {
          j=min(n/(n/i),m/(m/i));
          ans=1ll*ans*fpow(1ll*f[j]*ivf[i-1]%mod,1ll*(m/i)*(n/i)%(mod-1))%mod;
        }
      printf("%d\n",ans);
    }
  return 0;
}
posted @ 2018-12-20 22:40 smyjr 阅读(...) 评论(...) 编辑 收藏