2018 ICPC 焦作区域赛 Resistors in Parallel(找规律+大数)

传送门


题目大意

给出电阻的并联公式,规定一个含有平方因子的数的 1 R = 0 \frac{1}{R}=0 R1=0。定义一个数的阻值为其所有的因子阻值并联求出的结果,问 n n n以内并联后的最大的阻值是多少,输出分数形式

解题思路

打表不难知道在很大一段范围内答案是不变的,这体现在前几个数为 1 , 2 , 6 , 30 , 210 , 2310... 1,2,6,30,210,2310... 1,2,6,30,210,2310...,不难发现恰好是 1 , 1 ∗ 2 , 1 ∗ 2 ∗ 3 , 1 ∗ 2 ∗ 3 ∗ 5 , 1 ∗ 2 ∗ 3 ∗ 5 ∗ 7 , 1 ∗ 2 ∗ 3 ∗ 5 ∗ 7 ∗ 11... 1,1*2,1*2*3,1*2*3*5,1*2*3*5*7,1*2*3*5*7*11... 1,12,123,1235,12357,1235711...

但是这个时候如何去找规律呢,我曾尝试质因数分解,然后递推去求,但是这样最后的因子个数会达到 2 100 2^{100} 2100个,明显爆了。后来也想不出什么,去搜了题解,结果竟然是找了积性函数的规律。

f ( x ) f(x) f(x) x x x的因子并联的阻值,显然 f ( p ) = 1 1 + 1 p = p p + 1 f(p)=\frac{1}{1+\frac{1}{p}}=\frac{p}{p+1} f(p)=1+p11=p+1p,而 f ( p 1 p 2 ) = 1 1 + 1 p 1 + 1 p 1 p 2 + 1 p 2 = p 1 p 2 p 1 + 2 p 1 p 2 + p 2 = f ( p 1 ) f ( p 2 ) f(p_1p_2)=\frac{1}{1+\frac{1}{p_1}+\frac{1}{p_1p_2}+\frac{1}{p_2}}=\frac{p_1p_2}{p_1+2p_1p_2+p_2}=f(p_1)f(p_2) f(p1p2)=1+p11+p1p21+p211=p1+2p1p2+p2p1p2=f(p1)f(p2)

使用Java大数,那么这样就能愉快的预处理了!

import java.math.BigInteger;
import java.util.*;

public class Main {
    static long prime[] = new long[1005];
    static boolean vis[] = new boolean[1005];
    static BigInteger fz[] = new BigInteger[1005], fm[] = new BigInteger[1005];
    static BigInteger array[] = new BigInteger[1005];
    static int cnt, T, tot;

    static void getPrime() {
        for (int i = 0; i < 1005; i++) vis[i] = false;
        vis[1] = false;
        cnt = 0;
        for (int i = 2; i < 1005; i++) {
            if (!vis[i]) {
                prime[++cnt] = i;
            }
            for (int j = 1; j <= cnt && i * prime[j] < 1005; j++) {
                vis[(int) (i * prime[j])] = true;
                if (i % prime[j] == 0) break;
            }
        }
    }

    static void init() {
        BigInteger Max = BigInteger.ONE;
        for (int i = 1; i <= 100; i++) {
            Max = Max.multiply(BigInteger.TEN);
        }
        BigInteger res = BigInteger.ONE;
        fz[0] = BigInteger.ONE;
        fm[0] = BigInteger.ONE;
        array[0] = BigInteger.ZERO;
        for (int i = 1; i <= cnt; i++) {
            res = res.multiply(BigInteger.valueOf(prime[i]));
            //System.out.println(res);
            array[i] = res;
            if (res.compareTo(Max) > 0) {
                break;
            }
            fm[i] = fm[i - 1].multiply(BigInteger.valueOf(prime[i]).add(BigInteger.ONE));
            fz[i] = fz[i - 1].multiply(BigInteger.valueOf(prime[i]));
            //System.out.println(fz[i] + "/" + fm[i]);
            BigInteger gcd = fz[i].gcd(fm[i]);
            fz[i] = fz[i].divide(gcd);
            fm[i] = fm[i].divide(gcd);
        }
    }


    public static void main(String[] args) {
        getPrime();
        init();
        BigInteger n;
        Scanner scanner = new Scanner(System.in);
        T = scanner.nextInt();
        while ((T--) != 0) {
            n = scanner.nextBigInteger();
            if (n.compareTo(BigInteger.ONE) == 0) {
                System.out.println("1/1");
                continue;
            }
            int idx = 0;
            for (int i = 1; ; i++) {
                if (n.compareTo(array[i]) == 0) {
                    idx = i;
                    break;
                }
                if (n.compareTo(array[i - 1]) > 0 && n.compareTo(array[i]) < 0) {
                    idx = i - 1;
                    break;
                }
            }
            if (fz[idx] == fm[idx]) {
                System.out.println("1/1");
            } else System.out.println(fz[idx] + "/" + fm[idx]);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值