<互测 day> 11.6 T1 谜团 (mituan)

*PS: 没开long long被卡掉45分,果然出题人的话都不可信QAQ。。(学傻了)


与zqsz的互测题 T1 原题 POJ 1845


【题目描述】
谜团,是夜魇军团一名强大的战士。他来自远古,是一种不可思议的重力生命体,是来自原始黑暗的扭曲声音,在宇宙中的第一丝光线诞生前就存在的深渊化身。他能动用深渊之力将生物的身体污染,使之转化为自身的碎片“虚灵”。
“虚灵”为谜团所控,拥有一定的战斗力。更为恐怖的是,新产生的“虚灵”的 第一次攻击将吸收被攻击者内心的黑暗,从而分裂成若干个“虚灵”,之后的攻击将无法产生“虚灵”,新产生的“虚灵”第一次攻击仍可产生“虚灵”。 作为天辉军团智囊的你,需要知道某一时刻谜团最多可以产生多少“虚灵”,从而据
此来进行决策。具体的,谜团在 第一秒会转化一个单位,使之变成一个“虚灵”,由于能量消耗过大,谜团在之后的时间内将不再转化单位,使之变成“虚灵”。新产生的“虚灵”会在**下一秒**攻击一次,从而分裂出 m 个“虚灵”,原有的“虚灵”仍然存在,并且将不再产生虚灵。但新产生的“虚灵”可在下一秒攻击一次并分裂,之后也将不再产生“虚灵(具体见样例解释)。给定时间 t 和分裂数 m,请你告诉天辉军团的战士们,在 t 秒后,谜团拥有多少个“虚灵”。

答案可能很大,要求你模一个数 k(不保证 k 是质数)。

【输入格式】
第一行:三个整数 m,t,k,意义见题目描述
【输出格式】
第一行:t 秒后谜团拥有的能量体个数
【样例输入 1】
3 3 1000
【样例输出 1】
13
【样例输入 2】
3 5 1000
【样例输出 2】
121
【样例 1 解释】
m = 3, t = 5 时:
第一秒:谜团制造 1 个“虚灵”
第二秒:1 个“虚灵”分裂出 3 个“虚灵”,此时共有 4 个“虚灵”
第三秒:第 1 秒制造的 1 个“虚灵”将不能分裂,第 2 秒制造的 3 个“虚灵”,
每个“虚灵”分裂成 3 个“虚灵”,新分裂出的“虚灵”有 9 个,此时共有 13
个“虚灵”。
最终,总共有 13 个“虚灵”
【数据规模与约定】
保证 k,m,t 及答案在 int 范围内注意只有答案!!!)
原题意是求A^B的约数个数,因为出题人很良心(原话),所以就改为求首项为1,公比为m的等比数列的前n项和。 部分分


1~8 暴力枚举 


9~12 特殊性质:模数k是质数,因为等比数列的前n项和为 q^n-1/(q-1),除法的取模需要用到逆元,可以用费马小定理做。
a^(p-1) ≡ 1(mod p) ---> a^(p-2) ≡ 1/a (mod p)
1 ll ans=(multiply(T,M)-1)%K*multiply(K-2,M-1)%K%K;
2 printf("%lld",ans);



13~16(原话)特殊性质 k不是质数,但k与m互质,用扩展欧几里得求逆元。

但是,我用的扩展欧几里得是要求m-1在%k意义下的逆元啊喂!! 没有给部分分QAQ。

直接打exgcd可以的50分,前提是你开了long long ,但是我并没有开qwqqqq(让我自己一个人静一会)。
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 
 8 long long M,T,K,ans;
 9 
10 long long multiply(long long n,long long a)
11 {
12     long long sum=1,x=a;
13     while(n)
14     {
15         if(n&1) sum=sum%K*x%K%K;
16         x=x%K*x%K%K;
17         n>>=1;
18     }
19     return sum%K;
20 }
21 long long exgcd(long long a,long long b,long long &x,long long &y)
22 {
23     if(b==0)
24     {
25         x=1,y=0;
26         return a;
27     }
28     long long d=exgcd(b,a%b,y,x);
29     y=y-a/b*x;
30     return d;
31 }
32 int main()
33 {
34     freopen("mituan.in","r",stdin);
35     freopen("mituan.out","w",stdout);
36     scanf("%lld%lld%lld",&M,&T,&K);
37     {
38         long long x,y;
39         long long d=exgcd(M-1,K,x,y);
40         x%=K;
41         while(x<0) x+=K/d;
42         ans=(multiply(T,M)-1)%K*x%K%K;
43         if(ans<0) ans+=K;
44         printf("%lld",ans);
45     }
46     return 0;
47 }

17~20 没有特殊性质

 


正解 1 公式求逆元

安利一个之前看到的博客(为什么我当时没有好好看qwq)

http://blog.csdn.net/acdreamers/article/details/8220787

因为费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都会要求a与m互素。

但是我们有一个公式,若 b|a , 则ans即为(a/b)mod m 的答案:

 

证明:

(a/b)mod m = ans ;

a/b = ans + k * m ;

a = ans * b + k * m * b ;

a mod ( bm ) = ans * b ;

a mod ( bm ) / b = ans ;

得证。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 #define ull unsigned long long 
 8 
 9 ull M,T,K;
10 
11 ull multiply(ull n,ull a)
12 {
13     ull sum=1,x=a;
14     while(n)
15     {
16         if(n&1) sum=sum%K*x%K%K;
17         x=x%K*x%K%K;
18         n>>=1;
19     }
20     return sum%K;
21 }
22 int main()
23 {
24     freopen("mituan.in","r",stdin);
25     freopen("mituan.out","w",stdout);
26     scanf("%lld%lld%lld",&M,&T,&K);
27     K=K*(M-1);
28     ull ans=(multiply(T,M)-1)/(M-1);
29     printf("%lld",ans);
30     return 0;
31 }

 


 

正解 2 二分+递归

数学课上的求等比数列的前n项和有一个公式,emmm,公式有很多的变形,其中有一个变形为

若另 n 和 m 都等于 t/2 ,可知 



所以我们要求St,只需要每次二分的递归下去,在递归的过程中取模就可以了。

另外注意递归的时候要分t为奇数和偶数的情况,因为计算机里的/是会自动下取整的。


 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 #define ll long long
 7 
 8 ll M,T,K;
 9 
10 ll pow(ll a,ll n)
11 {
12     ll sum=1,x=a;
13     while(n)
14     {
15         if(n&1) sum=sum%K*x%K%K;
16         x=x%K*x%K%K;
17         n>>=1;
18     }
19     return sum;
20 }
21 
22 ll sum(ll q,ll t) // q^0+q^1+q^2+...+q^t --> S(t+1)
23 {
24     if(t==0) return 1;
25     if(t%2==0) return (sum(q,t/2-1)%K*(1+pow(q,t/2))%K%K+pow(q,t)%K)%K; 
26     //t 为偶数,说明S(t+1)没法直接通过S((t+1)/2)求得,就去求St,最后加上一个q^t次方 
27     else return (sum(q,t/2)%K*(1+pow(q,t/2+1))%K)%K; 
28     //t 为奇数,说明S(t+1)可以直接用(S(t+1)/2)求得,又S((t+1)/2)即为sum(q,t/2)(t/2下取整),可以直接求 (t/2+1即为(t+1)/2)  
29 }
30 int main()
31 {
32     freopen("mituan.in","r",stdin);
33     freopen("mituan.out","w",stdout);
34     scanf("%d%d%d",&M,&T,&K);
35     printf("%lld",sum(M,T-1));
36     return 0;
37 }

 




 

 


 

posted @ 2017-11-06 21:35  loi_maple  阅读(458)  评论(0编辑  收藏  举报