这题是个毒瘤题。你基本上要把你知道的数论算法都写上才能过。
题意简述
求 g ∑ i ∣ n C n i g^{\sum\limits_{i|n} C_{n}^{i}} gi∣n∑Cni,对 999911659 999911659 999911659取膜,其中 n , g < = 1 e 9 n,g<=1e9 n,g<=1e9。
思路
用扩展: L u c a s Lucas Lucas定理暴力求上面的 s i g m a sigma sigma,枚举因数是根号的, L u c a s Lucas Lucas是带log的,所以能过。然后无哦们用欧拉降幂对质数取膜。取膜完发现这 t m tm tm还不是个质数,所以要用中国剩余定理合并答案。
具体思路
step.1 假设我们能算上面那个 s i g m a sigma sigma
我们发现那个膜数是个质数。那么它的 p h i phi phi值就是 999911658 999911658 999911658。
然后我们把它分解,它 = 2 × 3 × 4679 × 35617 =2\times 3\times 4679\times 35617 =2×3×4679×35617。我们只要以这四个数作为膜数,分别求出答案,然后用中国剩余定理合并一下即珂。就相当于求出四个答案 a 1 , a 2 , a 3 , a 4 a_1,a_2,a_3,a_4 a1,a2,a3,a4,然后解方程:
x % 2 = a 1 x % 3 = a 2 x % 4679 = a 3 x % 35617 = a 4 x \% 2=a_1\\ x \% 3=a_2\\ x \% 4679=a_3\\ x \% 35617=a_4\\ x%2=a1x%3=a2x%4679=a3x%35617=a4
这个方程很容易解。珂是如何求上面那个 s i g m a sigma sigma呢?
step.2 上面那个 s i g m a sigma sigma
枚举
i
i
i的话是
O
(
n
)
O(\sqrt{n})
O(n),但是我们还要快速的求组合数。考虑扩展卢卡斯定理:
C
n
m
%
p
=
C
n
/
p
m
/
p
∗
C
n
%
p
m
%
p
%
p
C_{n}^{m}\%p=C_{n/p}^{m/p}*C_{n\%p}^{m\%p}\%p
Cnm%p=Cn/pm/p∗Cn%pm%p%p。然后对于后面那个
C
n
%
p
m
%
p
C_{n\%p}^{m\%p}
Cn%pm%p,我们只要预处理出
1
,
p
1,p
1,p之间阶乘和阶乘的逆元即珂。然后这个预处理不是每次都要预处理的,每次算之前预处理一下即珂。复杂度之和是
O
(
2
+
3
+
4679
+
35617
)
O(2+3+4679+35617)
O(2+3+4679+35617),珂以忽略不计。然后这样递归求的话,大概总的复杂度是
O
(
n
∗
l
o
g
(
n
)
+
2
+
3
+
4679
+
35617
)
O(\sqrt{n}*log(n)+2+3+4679+35617)
O(n∗log(n)+2+3+4679+35617)。是能过的。
好了,就到这里了,很短。但是要写很多东西。
step.3 快速幂 略
实现细节
- 特判 g = 999911659 g=999911659 g=999911659的情况,输出 0 0 0(要不然你就会 95 95 95到自闭)
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define P 44444
#define mod 999911658
#define int long long
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
int n,g;
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
void Input()
{
R1(n),R1(g);
}
int fac[P],ifac[P];//预处理阶乘和阶乘的逆元
int qpow(int a,int b,int m)//快速幂
{
int r=1;
while(b)
{
if (b&1) r=r*a%m;
a=a*a%m,b>>=1;
}
return r;
}
int C1(int n,int k,int p)//求一个组合数
{
if (n<k) return 0;
if (k==0 or k==n or n==0) return 1;
return fac[n]*ifac[k]%p*ifac[n-k]%p;
//仅限n,k<=p的情况,要不然有一个是0就除爆了
}
int C(int n,int k,int p)
{
if (n<k) return 0;
if (k==0 or k==n or n==0) return 1;
return C(n/p,k/p,p)*C1(n%p,k%p,p)%p;//Lucas定理
}
int calc(int n,int p)
{
fac[0]=1;
F(i,1,p) fac[i]=fac[i-1]*i%p;
ifac[p-1]=qpow(fac[p-1],p-2,p);
D(i,p-2,1) ifac[i]=ifac[i+1]*(i+1)%p;//预处理
int ans=0;
for(int i=1;i*i<=n;++i)//枚举因数
{
if (n%i==0)
{
int a=i;
ans+=C(n,a,p);//暴力计算
ans%=p;
if (i*i!=n)//别忘了判这个
{
int b=n/i;
ans+=C(n,b,p);//暴力计算
ans%=p;
}
}
}
return ans;
}
int a[5],b[5];
int CRT()
{
int ans=0;
F(i,1,4)
{
ans=(ans+a[i]*(mod/b[i])%mod*qpow(mod/b[i],b[i]-2,b[i]))%mod;
}
return ans;//扩展中国剩余定理
}
void Soviet()
{
if (g%(mod+1)==0)//会导致你WA掉的特判
{
puts("0");
return;
}
b[1]=2,b[2]=3,b[3]=4679,b[4]=35617;//四个膜数
F(i,1,4)
{
a[i]=calc(n,b[i]);//算出四个答案
}
printf("%lld\n",qpow(g,CRT(),mod+1)%(mod+1));
//别忘了最后输出g^CRT,而且膜数是mod+1,我令mod=
}
void IsMyWife()
{
Input();
Soviet();
}
#undef int //long long
}
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();getchar();
return 0;
}