[CF235E]Number Challenge

题目大意

给定a,b,c<=2000,求 ai=1bj=1ck=1d(ijk) ,其中 d(n)n

分析

嗯…题解给出一种鬼畜的解法,它事实上是能过的,我还不知道怎么分析复杂度。
我们知道d()是积性函数嘛,那么我们从大到小考虑质数,记忆化搜索。设f(a,b,c,pt)表示考虑到第pt个质数,三个循环的上界为a,b,c的式子的值。那么很显然 f(a,b,c,pt)=f(apri[pt]x,bpri[pt]y,cpri[pt]z,pt1)(x+y+z+1) 。然后用哈希存一下就过了…
注意边界条件,当pt到达0,说明所有的质数都考虑过了,那么我们返回1即可,因为即使上界不为1,那些数我们也取不了,因为所有质数都考虑过了。
用map超时,考试千万不要乱用…
另外有两种做法,一种是反演,一种是定理。

代码

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set>
#include<map>
#include<bitset>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
typedef long long ll;
typedef double db;
const int N=2e3+5,mo=1<<30,lim=10000000+5;
int pd[N],phi[N],pri[N],a,b,c,i,j,kan,tr[lim+5];
ll feat[lim+5];
void predo()
{
    int n=max(a,max(b,c)),t;
    fo(i,2,n)
    {
        if (!pd[i])
        {
            pri[++pri[0]]=i;
            phi[i]=i-1;
        }
        fo(j,1,pri[0])
        {
            if (1ll*pri[j]*i>n) break;
            t=pri[j]*i;
            pd[t]=1;
            phi[t]=phi[i]*phi[pri[j]];
            if (i%pri[j]==0)
                phi[t]=phi[i]*pri[j];
        }
    }
}
ll hash(int a,int b,int c,int pt)
{
    ll tp=1ll*a*2001*2001*2001+1ll*b*2001*2001+c*2001+pt;
    int k=tp%lim;
    while (tr[k]&&feat[k]!=tp) k=(k+1)%lim;
    if (!tr[k]) feat[k]=tp;
    return k;
}
int dfs(int a,int b,int c,int pt)
{
    //edge
    if (b<a) swap(a,b);if (c<a) swap(a,c);if (c<b) swap(b,c);
    if (tr[kan=hash(a,b,c,pt)]) return tr[kan];
    if (!pt) return 1;
    int d=pri[pt],na=a,nb,nc,i,j,k,ret=0;
    fo(i,0,20)
    {
        if (!na) break;
        nb=b;
        fo(j,0,20)
        {
            if (!nb) break;
            nc=c;
            fo(k,0,20)
            {
                if (!nc) break;
                ret=(ret+1ll*dfs(na,nb,nc,pt-1)*(i+j+k+1))%mo;
                nc/=d;
            }
            nb/=d;
        }
        na/=d;
    }
    tr[hash(a,b,c,pt)]=ret;
    return ret;
}
int main()
{
    freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    scanf("%d %d %d",&a,&b,&c);
    predo();
//  tr[trans(1,1,1,0)]=1;
    printf("%d",dfs(a,b,c,pri[0]));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值