首先发出题目链接:
链接:https://ac.nowcoder.com/acm/contest/634/D
来源:牛客网
涉及:扩展欧拉定理,快速幂
题目如下:
我去,题目又有求和,又是大数,果断放弃
对于这个题目,首先需要一些数学思考
小y有两组图片
第一组(蓝色):1 3 9 27 81…(3n-1)
第一组(红色):1 3 9 27 81…(3n-1)
可惜所有卡片混乱在一起,在这些混乱的卡片中每个3n-1都有两张卡片
每个3n-1可以化为3进制的数字
比如1(1),3(10),9(100),81(10000)…
所有卡片数字的总和m即为22222222222…2222(一共n个2)
(证明:3n-1前n项和化为3进制为11111111(一共n个1),回合卡片中每个数字都有红色和蓝色两张卡片,所以m等于3n-1前n项和的2倍)
现在的问题是组合卡片,求使得卡片和为1到m的所有卡片组合方案的和
从1到m每一个数字的都可以化为三进制数字h,每一个三进制数字h都有n位(不足n位补0)
然后考虑三进制数h每一位上面的数字(0,1,2):
当第i位上的数字为1时,表示这一位上需要1张3i-1数字的卡片(有且只有“红色”和“蓝色”两种选择);
当第i位上的数字为2时,表示这一位上需要2张3i-1数字的卡片(有且只有“红色和蓝色都要”这一种选择);
当第i位上的数字为0时,表示这一位上不需要3i-1数字的卡片(有且只有“不需要卡片”这一种选择);
(ps:此时有人会说当第i位上的数字不为0时,不能由多张3j(j<i-1)的卡片加起来得到吗?
然而,当第i位数字不为1时,你把3j(j<i-1)所有卡片加起来也凑不齐第i位的数字)
这个三进制数h的n个位置上的每一位数字都有可能是1,2或者3。为1时,会有两种选择;为2或者0时,会有一种选择。
假设有i个位置是1,j个位置是2,n-i-j个位置是0
则当n=n0时,所有的选择为
=
=
=
=
=
然后n不等于0,所以结果还要减去1,然后对p求余,于是所有的方案和为
对公式变形:
由于n非常大,根据扩展欧拉定理可知
(ps:是欧拉函数,的值为1~p中与p不互质的数的个数,求欧拉函数是有公式的,不知道欧拉函数和欧拉定理可以看看其他我的博客)
通过公式求欧拉函数值的代码如下
注意把(1-1.0/i)值化为浮点类型,如果是整型就错了,后来再化成long long类型
ll getDivisor(){
ll sum=p;
int i;
ll p1=p;
for(i=2;i*i<=p1;i++){
if(p1%i==0) sum=ll1*sum*(1-1.0/i);
while(p1%i==0) p1=p1/i;
}
if(p1!=1) sum=ll1*sum*(1-1.0/p1);
return sum;//sum为欧拉函数的值
}
所以现在只用通过大数求模的方式求出,得到之后用快速幂求,关于大数求模可以看看其他博客。
通过大数求模得到的值
ll getPow(){
stream<<str;//stream为stringstream类型
ll sum=0;
char c;
while(stream>>c)
sum=(10*sum+c-'0')%divisor;
stream.clear();
return sum+divisor;//其中sum为n对欧拉函数求余的值,divisor是欧拉函数的值
}
然后是快速幂的代码
ll qSort(){
ll x=4,sum=1;
while(pow1!=0){
if(pow1%2!=0) sum=sum*x%p;
pow1=pow1>>1;
x=x*x%p;
}
return sum-1+p;//sum是4的n次方的值(sum-1+p)对p取余即为答案
}
举个例子
当n=13,p=15
p的质因子为2和3和5,则
即答案为4(例子很简单,只要你实现了大数取模就可以)
代码如下:
#include <iostream>
#include <algorithm>
#include <string>
#include <sstream>
#define ll1 (ll)1
using namespace std;
typedef long long ll;
stringstream stream;//输出流,用来方便地将一个字符串拆成一个个字符
ll p,divisor,pow1; //p是题目所给,divisor是存p的质因子的个数,pow1是用来存幂数
string str;//用来存n的值
int t;
ll qSort(){//这个qsort写错了,不是快排,是快速幂,求4的n次方
ll x=4,sum=1;
while(pow1!=0){
if(pow1%2!=0) sum=sum*x%p;
pow1=pow1>>1;
x=x*x%p;
}
return sum-1+p;
//因为要算(4^n-1)%p的值,而(4^n-1)%p=(4^n%p-1+p)%p,所以要返回4^n%p-1+p,即返回sum-1+p
}
ll getPow(){//利用欧拉定理,将大数幂的指数的值降到10^9以下。
stream<<str;//将字符串存到stream去
ll sum=0;
char c;
while(stream>>c)//每次从stream拿一个字符出来
sum=(10*sum+c-'0')%divisor;//大数求余
stream.clear();
return sum+divisor;
}
ll getDivisor(){//分解质因子,得到p的质因子的个数
ll sum=p;
int i;
ll p1=p;
for(i=2;i*i<=p1;i++){
if(p1%i==0) sum=ll1*sum*(1-1.0/i);
while(p1%i==0) p1=p1/i;
}
if(p1!=1) sum=ll1*sum*(1-1.0/p1);
return sum;
}
int main(){
cin>>t;
while(t--){
cin>>str>>p;
divisor=getDivisor();
pow1=getPow();
cout<<qSort()%p<<endl;
}
return 0;
}