*PS: 没开long long被卡掉45分,果然出题人的话都不可信QAQ。。(学傻了)
与zqsz的互测题 T1 原题 POJ 1845
谜团,是夜魇军团一名强大的战士。他来自远古,是一种不可思议的重力生命体,是来自原始黑暗的扭曲声音,在宇宙中的第一丝光线诞生前就存在的深渊化身。他能动用深渊之力将生物的身体污染,使之转化为自身的碎片“虚灵”。
“虚灵”为谜团所控,拥有一定的战斗力。更为恐怖的是,新产生的“虚灵”的 第一次攻击将吸收被攻击者内心的黑暗,从而分裂成若干个“虚灵”,之后的攻击将无法产生“虚灵”,新产生的“虚灵”第一次攻击仍可产生“虚灵”。 作为天辉军团智囊的你,需要知道某一时刻谜团最多可以产生多少“虚灵”,从而据
此来进行决策。具体的,谜团在 第一秒会转化一个单位,使之变成一个“虚灵”,由于能量消耗过大,谜团在之后的时间内将不再转化单位,使之变成“虚灵”。新产生的“虚灵”会在下一秒攻击一次,从而分裂出 m 个“虚灵”,原有的“虚灵”仍然存在,并且将不再产生虚灵。但新产生的“虚灵”可在下一秒攻击一次并分裂,之后也将不再产生“虚灵”(具体见样例解释)。给定时间 t 和分裂数 m,请你告诉天辉军团的战士们,在 t 秒后,谜团拥有多少个“虚灵”。
答案可能很大,要求你模一个数 k(不保证 k 是质数)。
第一行:三个整数 m,t,k,意义见题目描述
第一行:t 秒后谜团拥有的能量体个数
3 3 1000
13
3 5 1000
121
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)
ll ans=(multiply(T,M)-1)%K*multiply(K-2,M-1)%K%K;
printf("%lld",ans);
13~16 (原话)特殊性质 k不是质数,但k与m互质,用扩展欧几里得求逆元。
但是,我用的扩展欧几里得是要求m-1在%k意义下的逆元啊喂!! 没有给部分分QAQ。
直接打exgcd可以的50分,前提是你开了long long ,但是我并没有开qwqqqq(让我自己一个人静一会)。
#include
#include
#include
#include
#include
using namespace std;
long long M,T,K,ans;
long long multiply(long long n,long long a)
{
long long sum=1,x=a;
while(n)
{
if(n&1) sum=sum%K*x%K%K;
x=x%K*x%K%K;
n>>=1;
}
return sum%K;
}
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
long long d=exgcd(b,a%b,y,x);
y=y-a/b*x;
return d;
}
int main()
{
freopen("mituan.in","r",stdin);
freopen("mituan.out","w",stdout);
scanf("%lld%lld%lld",&M,&T,&K);
{
long long x,y;
long long d=exgcd(M-1,K,x,y);
x%=K;
while(x<0) x+=K/d;
ans=(multiply(T,M)-1)%K*x%K%K;
if(ans<0) ans+=K;
printf("%lld",ans);
}
return 0;
}
17~20 没有特殊性质 qwq
1~20
安利一个之前看到的博客(为什么我当时没有好好看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 ;
得证。
#include
#include
#include
#include
#include
using namespace std;
#define ull unsigned long long
ull M,T,K;
ull multiply(ull n,ull a)
{
ull sum=1,x=a;
while(n)
{
if(n&1) sum=sum%K*x%K%K;
x=x%K*x%K%K;
n>>=1;
}
return sum%K;
}
int main()
{
freopen("mituan.in","r",stdin);
freopen("mituan.out","w",stdout);
scanf("%lld%lld%lld",&M,&T,&K);
K=K*(M-1);
ull ans=(multiply(T,M)-1)/(M-1);
printf("%lld",ans);
return 0;
}
数学课上的求等比数列的前n项和有一个公式,emmm,公式有很多的变形,其中有一个变形为
若另 n 和 m 都等于 t/2 ,可知
所以我们要求St,只需要每次二分的递归下去,在递归的过程中取模就可以了。
另外注意递归的时候要分t为奇数和偶数的情况,因为计算机里的/是会自动下取整的。
#include
#include
#include
#include
using namespace std;
#define ll long long
ll M,T,K;
ll pow(ll a,ll n)
{
ll sum=1,x=a;
while(n)
{
if(n&1) sum=sum%K*x%K%K;
x=x%K*x%K%K;
n>>=1;
}
return sum;
}
ll sum(ll q,ll t) // q^0+q^1+q^2+...+q^t --> S(t+1)
{
if(t==0) return 1;
if(t%2==0) return (sum(q,t/2-1)%K*(1+pow(q,t/2))%K%K+pow(q,t)%K)%K;
//t 为偶数,说明S(t+1)没法直接通过S((t+1)/2)求得,就去求St,最后加上一个q^t次方
else return (sum(q,t/2)%K*(1+pow(q,t/2+1))%K)%K;
//t 为奇数,说明S(t+1)可以直接用(S(t+1)/2)求得,又S((t+1)/2)即为sum(q,t/2)(t/2下取整),可以直接求 (t/2+1即为(t+1)/2)
}
int main()
{
freopen("mituan.in","r",stdin);
freopen("mituan.out","w",stdout);
scanf("%d%d%d",&M,&T,&K);
printf("%lld",sum(M,T-1));
return 0;
}
PS:刚刚把这篇博客搬到了博客园里,果然背景什么的都好漂亮(捂脸)。。虽然写的时候很麻烦。。。但是好看啊!!! 链接:http://www.cnblogs.com/maple-kingdom/