• Home
  • About
    • dancingline photo

      dancingline

      Moon is a minimal, one column jekyll theme for your blog.

    • Learn More
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

xdoj1290小国的复仇 数学

27 May 2018

Reading time ~1 minute

臭不要脸地水了一波xdu校赛,一心暴力果断T,后来才差不多能看懂解释

题目链接:http://acm.xidian.edu.cn/problem.php?id=1290

题目描述

众所周知,汀老师是XDUACM实验室最优秀的人,无论是学习还是打游戏。今天他突然想到一个好玩的游戏。规则是这样的,在游戏中他要得到n个小国,初始的时候小国和小杰各有1个。经过了很久的修炼,汀老师学会了两种魔法,他每次可以动用自己的智慧来使用魔法。

第一个魔法:(小杰变小国)可以使用自己的智慧,复制和当前小杰一样数量的小国出来;

第二个魔法:(小国大爆发)可以将当前的小杰变成和小国的数量一样,然后小国的数量加倍!

因为汀老师的智力是无限多的,他不关心花掉的智力大小。但是好学的汀老师想尽快得到n个小国,使得能有更多的时间去读paper和打比赛。他想问问你,最少需要使用多少次魔法可以恰好得到n个小国。

得到了n个小国后,汀老师去学习,但是小国们基因突变在电脑里越来越多!他们来组织汀老师学习,现在告诉汀老师我要得到更多的同伴!

输入

多组数据,第一行一个正整数T(T<=100000)表示数据组数。

接下来T行,每行一个正整数n(n<=10^6)。

输出

对于每组数据输出一个整数,表示得到n个小国汀老师最少需要使用多少次膜法。

样例输入

2
1
3

样例输出

0
2

思路

很明显,对于任意n>1,代表小国和小杰数目的状态(n,m)可以由一个之前的状态通过一次魔法二和若干次魔法一得到(可以为零次)。

我们定义num[x]表示生成x个小国需要使用多少次魔法,假设x个小国的状态可以由一个之前的p个小国的状态经过1次魔法二和c(c>=0)次魔法一得到,则有

\[\begin{equation} num[x=(2+c)p] = num[p]+c+1 \end{equation}\]

我们将1次魔法二和c次魔法一定义为一个基本变换,从初态到终态可以经过若干次基本变换得到,现在问题转换为如何控制基本变换的次数使得最终得到的num[x]最小。通过观察上面的式子我们可以发现求num[x]的过程其实是不断对x进行因式分解,设\(x=e_1e_2e_3 \dots e_k\) ,则有

\[\begin{equation} num[x]=\sum_{i=1}^k e_i-1 \end{equation}\]

如何使得上式中的累加和最小,这时我们猜测因子能拆就拆最好,也就是拆成全是素因子(勉强把1也当成素数好了,反正它不影响结果),下面简单证明一下拆比不拆好。

设\(x=e_1e_2\),不拆有\(num[x]=e_1e_2-1\),拆有\(num[x]=e_1+e_2-2\),两式相减得到:

\[\begin{equation} e_1e_2-1-(e_1+e_2-2)=e_1e_2-(e_1+e_2)+1=(e_1-1)(e_2-1)>=0 \end{equation}\]

因此将x分解质因数,\(x=p_1^{\alpha_1}p_2^{\alpha_2}p_3^{\alpha_3} \dots p_k^{\alpha_k}\),最终结果应该为

\[\begin{equation} ans_x=\sum_{i=1}^k\alpha_i(p_i-1) \end{equation}\]

至于最后这道题是分解质因数还是像DP一样去递推就看个人喜好了。

另外有一个非常重要的问题,他这个题,卡cin!!!,T了不知道多少发,所以还是换成scanf好了。

贴两条代码 :

//直接分解质因数算
#include <bits/stdc++.h>
using namespace std;
int cal (int n)
{
    int ans=0, s=sqrt(n);
    for (int i=2; i<=s; i++)
        while (n%i == 0)
        {
            ans += i-1;
            n /= i;
        }
    ans += n-1;
    return ans;
}
int main ()
{
    int n, t;
    scanf ("%d", &t);
    while (t--)
    {
        scanf ("%d", &n);
        printf ("%d\n", cal(n));
    }
    return 0;
}
//DP的形式,利用最小质因数转移状态
#include <bits/stdc++.h>
using namespace std;
int f[1000001]={0};
bool is_p[1000001]={0};
vector<int> v;
int main ()
{
    int t, n;
    for (int i=2; i<=1000; i++) //质数筛 只需要筛出sqrt(n)范围的就可以了
    {
        if (!is_p[i])
            v.push_back(i);
        for (auto x:v)
            if (x*i > 1000)
                break;
            else
                is_p[x*i] = true;
    }
    for (int i=2; i<=1000000; i++)  //时限问题,先预处理出来
    {
        f[i] = i-1;  //先默认这是个质数
        for (auto j:v)  //找最小质因数转移状态
            if (i%j == 0)
            {
                f[i] = f[i/j]+j-1;
                break;
            }
            else if (j*j > i) break;
    }
    scanf ("%d", &t);
    while (t--)
    {
        scanf ("%d", &n);
        printf ("%d\n", f[n]);
    }
    return 0;
}


ACM数学 Share Tweet +1