*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)
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<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
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
正解 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 ;
得證。
代碼
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
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;
}
正解 2 二分+遞歸
數學課上的求等比數列的前n項和有一個公式,emmm,公式有很多的變形,其中有一個變形爲
若另 n 和 m 都等於 t/2 ,可知
所以我們要求St,只需要每次二分的遞歸下去,在遞歸的過程中取模就可以了。
另外注意遞歸的時候要分t爲奇數和偶數的情況,因爲計算機裏的/是會自動下取整的。
代碼
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
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/