[SDOI2017]数字表格

Description

对于斐波那契 \(F\)\(T\) 次询问,每次给定 \(n\)\(m\) ,求

\[\prod_{i=1}^n \prod_{j=1}^m F_{gcd(i,j)} \]

Solution

一开始就很蠢地把 \(F_{gcd(i,j)}\) 拆成 \(gcd(F_{i},F_{j})\)。虽然确实有这个结论,但是显然推不下去,而又没有考虑回溯……服了

其实不拆的话相当好推,就常规套路就行了。令 \(c=min(n,m)\)

\[\begin{align} (*)=\prod_{d=1}^c F_d^{\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)=d]} \end{align} \]

然后立即发现右上角是一个熟悉得不能再熟悉的式子,直接跳过中间步骤,得到

\[\begin{align} (1)&=\prod_{d=1}^c F_d^{\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor} \mu(k)\lfloor \frac{n}{kd} \rfloor\lfloor \frac{m}{kd} \rfloor} \\ &=\prod_{T=1}^c \prod_{d|T} F_d^{\mu(\frac{T}{d})\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor} \\ &=\prod_{T=1}^c \Big(\prod_{d|T} F_d^{\mu(\frac{T}{d})} \Big)^{\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor} \end{align} \]

随便处理一下,复杂度 \(O(T\sqrt n\log n+n \log n)\),但好像我的跑得很慢?

#include<stdio.h>
#define Mod p
#define ll long long
#define N 1000007

inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

int p=(1e9)+7;

ll qpow(ll x,ll y){
    ll ret=1,cnt=0;
    while(y>=(1ll<<cnt)){
        if(y&(1ll<<cnt)) ret=ret*x%p;
        x=x*x%p,cnt++;
    }
    return ret;
}

ll f[N][3],g[N];
int mu[N],P[N],cnt=0;
bool mk[N];

inline int min(int x,int y){return x<y? x:y;}

int main(){
    f[0][2]=0,f[1][2]=1;
    for(int i=2;i<N;i++) f[i][2]=(f[i-1][2]+f[i-2][2])%Mod;
    for(int i=0;i<N;i++) f[i][0]=qpow(f[i][2],p-2);
    mu[1]=1;
    for(int i=2;i<N;i++){
        if(!mk[i])
            P[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&P[j]*i<N;j++){
            mk[i*P[j]]=1;
            if(i%P[j]) mu[i*P[j]]=-mu[i];
            else break;
        }
    }
    for(int i=0;i<N;i++) g[i]=f[i][1]=1;
    for(int i=1;i<N;i++)
        for(int j=1;j*i<N;j++)
            g[i*j]=g[i*j]*f[i][mu[j]+1]%Mod;
    for(int i=2;i<N;i++) g[i]=g[i]*g[i-1]%Mod;
    int T=read();
    while(T--){
        int n=read(),m=read();
        int rg=min(n,m);
        ll ans=1;
        for(int l=1,r;l<=rg;l=r+1){
            r=min(n/(n/l),m/(m/l));
            ans=ans*qpow(g[r]*qpow(g[l-1],p-2)%p,1ll*(n/l)*(m/l))%p;
        }
        printf("%lld\n",ans);
    }
}
posted @ 2021-03-30 19:46  Kreap  阅读(44)  评论(0编辑  收藏  举报