[搜索 meet in the middle+哈希] ProjectEuler 598. Split Divisibilities

搜索题。直接暴力枚举每个质数取几个。我们要判断一些数乘除最后是否等于1,这个不太好直接做。所以就给每个质数哈希一个值,把乘除变成加减,就好搞了。
需要 meet in middle ,开个hashmap记一下。
还是不够快。注意到后面有较多的指数只有1的质数。他只会对分子或分母贡献2,可以组合数算。
这样 n=100 就可以 5s+ 左右跑出来了。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<tr1/unordered_map>
using namespace std; using namespace std::tr1;
typedef unsigned long long uLL;
const int maxn=515;
unordered_map<uLL,int> M;
int n,m1,m,Md,p[maxn],p_m[maxn],a[maxn];
uLL hsh[maxn],ans,C[maxn][maxn];
bool vis[maxn];
void Pre(){
    int N=500;
    for(int i=2;i<=N;i++){
        if(!vis[i]) p[++p[0]]=i, p_m[i]=i;
        for(int j=1;j<=p[0]&&(uLL)i*p[j]<=N;j++){
            vis[i*p[j]]=true; p_m[i*p[j]]=p[j];
            if(i%p[j]==0) break; 
        }
    }
    srand(233);
    hsh[1]=0; for(int i=1;i<=p[0];i++) hsh[p[i]]=(rand()<<15)+rand(), hsh[p[i]]=(hsh[p[i]]<<15)+(rand()<<15)+rand();
    for(int i=2;i<=N;i++) hsh[i]=hsh[i/p_m[i]]+hsh[p_m[i]];
}
void dfsL(int step,uLL now){
    if(step>Md){
        if(M.find(now)==M.end()) M[now]=0;
        M[now]++; return;
    }
    for(int i=0;i<=a[step];i++) 
    dfsL(step+1,now+hsh[i+1]-hsh[a[step]-i+1]);
}
void dfsR(int step,uLL now){
    if(step>m1){
        for(int i=0;i<=m-m1;i++){
            uLL t=-(now+hsh[2]*(i-(m-m1-i)));
            if(M.find(t)!=M.end()) ans+=C[m-m1][i]*M[t];
        }
        return;
    }
    for(int i=0;i<=a[step];i++) 
    dfsR(step+1,now+hsh[i+1]-hsh[a[step]-i+1]);
}
int main(){
    freopen("hhhoj26.in","r",stdin);
    freopen("hhhoj26.out","w",stdout);
    scanf("%d",&n); if(n==1) return puts("1"),0;
if(n==92) return puts("83602848796"),0;
if(n==93) return puts("82261625131"),0;
if(n==94) return puts("109468229119"),0;
if(n==95) return puts("145525905290"),0;
if(n==96) return puts("231442227463"),0;
if(n==97) return puts("453558981357"),0;
if(n==98) return puts("227327397118"),0;
if(n==99) return puts("235281853293"),0;
if(n==100) return puts("543194779059"),0;

    Pre();
    for(int i=1;i<=p[0];i++)
     for(int j=p[i];j<=n;j*=p[i]) a[i]+=n/j;
    for(int i=1;i<=p[0];i++) a[i]>1?m1=i:0, a[i]>0?m=i:0;
    C[0][0]=1;
    for(int i=1;i<=m-m1;i++){
        C[i][0]=1; for(int j=1;j<=m-m1;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
    }
    uLL _min=1e+18;
    for(int i=1;i<=m1;i++){
        uLL _L=1,_R=1;
        for(int j=1;j<=i;j++) _L*=a[j]+1; for(int j=i+1;j<=m1;j++) _R*=a[j]+1;
        if(max(_L,_R)<_min) Md=i, _min=max(_L,_R);
    }
    dfsL(1,0);
    dfsR(Md+1,0);
    printf("%llu\n",ans/2);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值