洛谷 2480 bzoj 1951 [SDOI2010]古代猪文 题解

33 篇文章 0 订阅
博客介绍了SDOI2010古代猪文问题的解题思路,主要涉及数论算法,包括Lucas定理、欧拉降幂和中国剩余定理。通过扩展Lucas定理暴力计算并结合快速幂优化,解决模运算下的组合数求解,最后解释了如何应用中国剩余定理合并答案。
摘要由CSDN通过智能技术生成

博客观赏效果更佳

这题是个毒瘤题。你基本上要把你知道的数论算法都写上才能过。

题意简述

g ∑ i ∣ n C n i g^{\sum\limits_{i|n} C_{n}^{i}} ginCni,对 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/pCn%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 快速幂 略

实现细节

  1. 特判 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值