Aladdin and the Flying Carpet LightOJ - 1341

该博客主要讨论了一种算法优化策略,针对给定的大整数,寻找其平方根内的因子组合,原暴力算法复杂度过高,无法通过所有测试用例。博主提出了使用Pollard Rho算法分解质因数,并通过深度优先搜索枚举所有因子组合,以期望达到O(n^(1/4) + ln(n))的时间复杂度。在代码实现中,包括了二次探测原理检验、Miller-Rabin素性测试等技术,并给出了完整的C++代码实现。
摘要由CSDN通过智能技术生成

题目链接:https://vjudge.net/problem/LightOJ-1341
思路:开始想了 n \sqrt{n} n 的暴力算法,发现复杂度大概在4e9显然不能过。考虑分解质因数(试除法复杂度也不够,Pollard Rho 算法 n 1 / 4 n^{1/4} n1/4复杂度可行),我们对分解完的质因数进行dfs枚举原数中所有因数(因数个数期望位 l n ( n ) ln(n) ln(n)),进行计数。复杂度期望 O ( n 1 / 4 + l n ( n ) ) O(n^{1/4}+ln(n)) O(n1/4+ln(n))
代码:

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
using namespace std;
ll max_factor;
struct BigIntegerFactor {
    const static int N = 1e6 + 7;
    const static ll inf=0x3f3f3f3f3f3f3f3f;
    ll prime[N], p[N], fac[N], sz, cnt; //多组输入注意初始化cnt = 0
    inline ll mul(ll a, ll b, ll mod) {          //WA了尝试改为__int128或慢速乘
        if (mod <= 1000000000)
            return a * b % mod;
        return (a * b - (ll)((long double)a / mod * b + 1e-8) * mod + mod) % mod;
    }
    void init(int maxn) //线性筛 
	{
        int tot = 0;
        sz = maxn - 1;
        for (int i = 1; i <= sz; ++i)
            p[i] = i;
        for (int i = 2; i <= sz; ++i) {
            if (p[i] == i)
                prime[tot++] = i;
            for (int j = 0; j < tot && 1ll * i * prime[j] <= sz; ++j) {
                p[i * prime[j]] = prime[j];
                if (i % prime[j] == 0)
                    break;
            }
        }
    }
    ll qpow(ll a, ll x, ll mod)
	{
        ll res = 1ll;
        while (x) {
            if (x & 1)
                res = mul(res, a, mod);
            a = mul(a, a, mod);
            x >>= 1;
        }
        return res;
    }
    bool check(ll a, ll n) {                     //二次探测原理检验n
        ll t = 0, u = n - 1;
        while (!(u & 1))
            t++, u >>= 1;
        ll x = qpow(a, u, n), xx = 0;
        while (t--) {
            xx = mul(x, x, n);
            if (xx == 1 && x != 1 && x != n - 1)
                return false;
            x = xx;
        }
        return xx == 1;
    }
    bool miller(ll n, int k) {
        if (n == 2)
            return true;
        if (n < 2 || !(n & 1))
            return false;
        if (n <= sz)
            return p[n] == n;
        for (int i = 0; i <= k; ++i) {            //测试k次
            if (!check(rand() % (n - 1) + 1, n))
                return false;
        }
        return true;
    }
    inline ll gcd(ll a, ll b) {
        return b == 0 ? a : gcd(b, a % b);
    }
    inline ll Abs(ll x) {
        return x < 0 ? -x : x;
    }
    ll Pollard_rho(ll n) {                 //基于路径倍增的Pollard_Rho算法
        ll s = 0, t = 0, c = rand() % (n - 1) + 1, v = 1, ed = 1;
        while (1) {
            for (int i = 1; i <= ed; ++i) {
                t = (mul(t, t, n) + c) % n;
                v = mul(v, Abs(t - s), n);
                if (i % 127 == 0) {
                    ll d = gcd(v, n);
                    if (d > 1)
                        return d;
                }
            }
            ll d = gcd(v, n);

            if (d > 1)
                return d;
            s = t;
            v = 1;
            ed <<= 1;
        }
    }
    void getfactor(ll n) {                         //得到所有的质因子(可能有重复的)
        if (n <= sz) {
            while (n != 1)
                fac[cnt ++ ] = p[n], n /= p[n];
            max_factor = max_factor > p[n] ? max_factor : p[n];
            return;
        }
        if (miller(n, 6)) {
            fac[cnt ++ ] = n;
            max_factor = max_factor > n ? max_factor : n;
        }
        else {
            ll d = n;
            while (d >= n)
                d = Pollard_rho(n);
            getfactor(d);
            getfactor(n / d);
        }
        return ;
    }
    ll ans,num;
    pair<ll,ll> pp[N];
    void dfs(ll x,ll a,ll b,ll now)
    {
    	if(x>=num)
    	{
    		//printf("ans:%lld %lld %lld\n",x,now,a/now);
    		if(min(now,a/now)>=b)
    		{
    			if(now!=a/now) ans++;
    			//else ans+=2;//(注意不能是正方形)
			}
    		return ;
		}
		dfs(x+1,a,b,now);
		for(int i=1;i<=pp[x].se;i++)
		{
			now*=pp[x].fi;
			dfs(x+1,a,b,now);
		}
	}
    //这样写不太好初始化。。。。WA了好久 
	ll slove(ll a,ll b)
	{
		cnt=0;ans=0;num=0;
		getfactor(a);
		sort(fac,fac+cnt);
		fac[cnt]=-1;
		pp[num].se=0;
		for(int i=0;i<=cnt;i++)
		{
			pp[num].fi=fac[i];
			pp[num].se++;
			if(fac[i]!=fac[i+1])
			{
				num++;
				pp[num].se=0;
			}
		}
		dfs(0,a,b,1);
		return ans/2;
	}
}Q;

int main()
{
    ll t;
    scanf("%lld",&t);
    ll cas=0;
    while(t--)
    {
    	ll a,b;
    	scanf("%lld%lld",&a,&b);
    	if(a==1||b>a/b)
    	{
    		printf("Case %lld: 0\n",++cas);
    		continue;
		}
    	printf("Case %lld: %lld\n",++cas,Q.slove(a,b));
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值