2016.5.21纪中模拟赛

题目:https://jzoj.net/junior/#contest/problems/1309


T1:

题目很简单,其实就是对于输入的x的[0,1]两种情况,做题目要求你做的事情即可,简单模拟。

T2:

这题就是深搜,只要加一个“判断当前得到的分值是否大于已存的最小分值”的优化即可(然而我考试时因为调试大数据,从而输出的输出语句忘记屏蔽,导致爆0,我的心其实在出成绩的一刻是碎的)

T3:

题目大意:

定义^^为超级幂符号。如a^^b表示a^(a^(a^(a^a^(...)))),共b个a。先要求你求出a^^b的个位数(接下来求解的是对于一个a^^b个位数的结果,然而题目中是有n个a^^b相乘的结果,但是其实只要求出一个a^^b的值,不管题目怎么变,都可以迎刃而解了)

分析:

因a,b的值都很大,如果纯粹模拟答案的话最多拿20分。所以我们需要旁敲侧击,用另一种找规律的方法来求解。

因题目中规定a的尾数不可能为2,4,8,则我们只需考虑0,1,3,5,6,7,9了,然而我们通过简单的寻找规律得知,当a的尾数为0,1,5,6,9时只需乘以对应a的尾数即可,所以现在我们需考虑的尾数就是3,7了。

因:3^4 mod 10=1,且7^4 mod 10=1。

(当尾数为3时,我们可以知道a mod 4=1或3.如下{}之间证明:

{任何数可以被表示为a*100+b。例如624:a=6,b=24,而我们知道,任何整百数都能被4整除,所以我们只需判断b是否能被4整除,也就是判断一个数的末尾两位能被4整除即可。明白了这个定律,我们又可以在此定律的基础上繁衍出如下一个定律:

x=a*100+b,则我们只需求出b mod 4的值则为x mod 4的值了。而我们知道b=_3,而当空格内的值为1的时候,结果(以后简称为ans)=1;为2的时候,ans=3;为3的时候,ans=1,为4的时候,ans=3……以此规律我们可以得知前面所说的a mod 4为何一定等于1或3了。})

根据3^4mod 10=1这一结论,我们可以得知,当a mod 4=3的时候:3^3^3^3^3...^3最后的结果一定等于7.而当a mod 4=1的时候则是1^1^1^1^1..^1最后结果为1.所以我们只需判断a mod 4的值就可以求出最后的答案了(这里其实是把a简化成1,3了,因为不管a的指数有多大,它能被4除的部分的结果都是1,只有与4除的余数相乘才会得到不一样的值,而且题目里的b值完全是毫无意义的,设那么大只是用来提高逼格。)

而当尾数为7的时候则与尾数为3的时候则与尾数为3的结果相反。

T4:

题目大意:

找出[L..R]区间里的约数国王。约数国王:一个大于1的整数n,如果它约数的个数比1~n-1的每个整数的约数的个数都要多,那么我们就称它为约数国王。


分析:

做这个题目首先想到的肯定是暴力求解:

就是把1~n的每个数的约数求出来,然后用max表示1~i-1的拥有最多约数的数。然后如果当前第i个数大于max的话则此约数必定是约束国王(这里的i应在[L..R]内)。

核心代码:

for i:=1 to r do
begin
        if (i mod 2=1) or ((i>48) and (i mod 10<>0)) then continue;
        tot:=0;
        for j:=1 to trunc(sqrt(i)) do
                if i mod j=0 then inc(tot);
        if sqrt(i)=trunc(sqrt(i)) then tot:=tot*2-1 else tot:=tot*2;<span style="font-family: KaiTi_GB2312;">
        if tot>max then
        begin
                max:=tot;
                if i>=l then
                begin
                        inc(a[0]);
                        a[a[0]]:=i;
                end;
        end;
end;



 

但是这样子只能拿到50分,因为r的取值范围是maxint64,所以我们必须考虑另一种方法:

根据50分的方法,我们得知,只要第i个数的约数个数大于1~i-1约数个数,则其为约数国王,那么现在我们需要把思路倒过来,求出拥有k个约数的最小整数数,则可以通过动态规划来求出拥有i个约数的最小正整数。


设c[i]表示有i个约数的最小正整数,如果c[i]<c[i+1],c[i+2],c[i+3]……c[i+k],则c[i]为约数国王(然而这里的k是一个常数,当对于不同的数据的时候k都不一样,但实质上我们可以找到一个确切的常数k,使得刚好可以过最大数据,又不会耗时太多,这个需自己尝试,不提供常数的值)


然而需要求c[i]是个大麻烦,我们又可以考虑动态规划的方法:


我们最容易想到的就是设f[x,y]表示有x个最小的连续的质因数,y个因数的最小值。我们可以想到动态转移方程为:

f[x,y]:=min{f[x-1,y div (k+1)]*p_x^k | (k+1|y)}(x的取值范围为log2y,p_x指的是第x个最小的质数。(这里就不解释为什么动态规划决策的由来了,接下来下一个转移方程式会详细说明))


x的取值范围为什么是log2y呢?

想要明白这个我们需先知道一个定理:设factor=p1^q1*p2^q2*p3^q3*……*pi^qi,则factor的因数为(q1+1)*(q2+1)*(q3+1)*……*(qi+1)。

当我们把factor拆分为最优的情况(最少因数的情况)也就是至少qi=1的情况(也就是像2*3*5*7*11……这类的x,如果qi<1的话,则没有x个连续质因数了)这种情况至少有(1+1)*(1+1)*(1+1)*……(1+1)亦即,设factor有x个质因数,则因数个数为2^x=y,反推之,易证x=log2y。


但是这样子去做的话,依然只能拿70分——然而为什么只能拿70分,这是一个值得思考的问题——因为当我们知道时间复杂度为O(x*y)的时候,我们还多了一个k,这个k因为我们不可能把y的因数先算出来再枚举,所以我们需要1~y的一一枚举,来确定y的因数以及最优值,然而这样子时间复杂度就为O(y*y*log2y)。


所以我们可以想到用逆推,也就是通过f[x,y]去求f[x+1,y*(k+1)],这是因为我们不可能快速通过i去推i的因数,但我们可以通过i去推i的倍数:


状态转移方程为:

f[x+1,y*(k+1)]:=min{f[x,y]*p_x^k}

现在来证明一下状态转移方程的由来:


因为不管f[x,y]的值为多少,所乘的k个p_x都会与对应的y个因数相乘,再加上原来的y个因数,所以所得到的就是y*(k+1)了(p_x指第p大的质因数)


例:

f[1,4]=8. 

factor[1,2,4,8] in 8.

f[2,12]=72.

factor[1,2,3,4,6,8,9,12,18,24,36,72] in 72


x=1,y=4,k=2


  f[x,y]=>f[x+1,y*(k+1)]

=f[1,4]=>f[2,12]


    1 2 4 8

3=       3 6 12 24

3*3=    9 18 36 72 --> 


12=4+4+4=4*3=4*(k+1)=y*(k+1).


得知状态转移方程的由来后我们还需注意几个问题:

一、对于最小的第x个质数,这是一个固定的值,所以我们可以通过设定常数数组来求解,可以减少编程复杂度.

二、对于求出的每个f[x,y],则c[y]的值就等于min{f[x,y]}.

三、对于f或c数组的初值都需要设定为maxint64.

四、对于f数组的初值,f[1,i]就不说了,自己探讨一下吧。


效率:


而我们还可以进一步优化,当我们用了滚动数组后效率为:


不仅空间大大提高,而且时间也提升了一些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值