1 条题解

  • 0
    @ 2025-4-27 11:04:24

    BZOJ 2313 分组 题解


    前置知识

    1. 威尔逊定理;
    2. 中国剩余定理;
    3. 费马小定理(欧拉定理);
    4. 组合数学。

    题意简化

    给定 nnmm,设 kk 为将 nn 个数划分成多个大小一样的集合的方案数,求 mk(mod109401)m^k \pmod{10^9 - 401}


    题目分析

    nn 划分成的集合大小为 pp,那么这时方案数就是:

    $$\begin{aligned} & \frac{\prod_{i=1}^{\frac{n}{p}}\begin{pmatrix} ip \\ p \end{pmatrix}}{\frac{n}{p}!} \\ & = \frac{\prod_{i=1}^{\frac{n}{p}}\frac{(ip)!}{[(i-1)p]!p!}}{\frac{n}{p}!} \\ & = \frac{\prod_{i=1}^{\frac{n}{p}}\frac{(ip)!}{[(i-1)p]!}}{\frac{n}{p}!(p!)^{\frac{n}{p}}} \\ & = \frac{n!}{\frac{n}{p}!(p!)^{\frac{n}{p}}} \\ \end{aligned} $$

    而因为大小要相等,我们知道 pp 肯定是 nn 的约数,所以总方案数 kk 就是:

    $$k = \sum_{p|n}\frac{n!}{\frac{n}{p}!(p!)^{\frac{n}{p}}} $$

    P=109401P=10^9 - 401 那么最后求得的答案为:

    $$m^{\sum_{p|n}\frac{n!}{\frac{n}{p}!(p!)^{\frac{n}{p}}}} \pmod P \\ $$

    而我们知道根据欧拉定理,这个大指数就可以降幂,变成形如下面的式子:

    $$\begin{aligned} & m^{\sum_{p|n}\frac{n!}{\frac{n}{p}!(p!)^{\frac{n}{p}}}} \pmod P \\ & = m^{\sum_{p|n}\frac{n!}{\frac{n}{p}!(p!)^{\frac{n}{p}}}\pmod {\varphi{(P)}}} \pmod P \\ \end{aligned} $$

    同时我们又发现作为模数的 10940110^9-401 是一个质数,那么欧拉定理退化成费马小定理:

    $$\begin{aligned} & m^{\sum_{p|n}\frac{n!}{\frac{n}{p}!(p!)^{\frac{n}{p}}}\pmod {\varphi{(P)}}} \pmod P \\ & = m^{\sum_{p|n}\frac{n!}{\frac{n}{p}!(p!)^{\frac{n}{p}}}\pmod {P-1}} \pmod P \\ \end{aligned} $$

    看着这个式子似乎还是不太好做,那我们想一下有什么与阶乘相关的数论知识:明显是威尔逊定理。但是又出现了一个问题,威尔逊定理要求模数是质数,而 P1P-1 明显不是质数,我们需要一种方法转换它,这时候就想到了性质非常契合的中国剩余定理:我们把 P1P-1 拆成几个质数 p1pnp_1 \dots p_n,然后就可以通过下面的式子算出来:

    XX 是我们要求的指数,YYXX 降幂之后的值,则 XY(modP1)X \equiv Y \pmod{P-1},它们满足以下线性同组方程组:

    $$\forall i \in [1,n],p_i | (P-1) \\ \begin{cases} X \equiv y_1 \pmod {p_1} \\ X \equiv y_2 \pmod {p_2} \\ \cdots \\ X \equiv y_n \pmod {p_n} \\ \end{cases} $$

    那么我们对每个质数 pi,i[1,n]p_i,i \in [1,n] 分别把 yiy_i 求出来,最后再放到一起用中国剩余定理合并,就可以得出 YY

    我们处理完,发现 P1=2×13×5281×7283P-1 = 2 \times 13 \times 5281 \times 7283,也就是说 {pi}={2,13,5281,7283}\{ p_i \} =\{2,13,5281,7283\}

    那么我们只需要求出对于每个 pip_i,$\frac{n!}{\frac{n}{p}!(p!)^{\frac{n}{p}}} \pmod {p_i}$ 的值是多少,容易发现这是威尔逊定理的模版。


    CODE

    #include<bits/stdc++.h>
    #define FOR(i,a,b) for(int i=(a);i<=(int)(b);++i)
    #define DOR(i,a,b) for(int i=(a);i>=(int)(b);--i)
    #define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
    #define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
    using namespace std;
    constexpr int N=1e4+10,MOD[5]= {999999599,2,13,5281,7283};
    namespace Modular_Arithmetic {
    #define Toadd(a,b) ((a)=add((a),(b)))
    #define Tomul(a,b) ((a)=mul((a),(b)))
    #define toadd(x,a,b) ((a)=(x).add((a),(b)))
    #define tomul(x,a,b) ((a)=(x).mul((a),(b)))
    	struct Modular {
    		int MOD;
    		int fac[N];
    		template<typename T1,typename T2>constexpr auto add(T1 a,T2 b) {
    			return a+b>=MOD?a+b-MOD:a+b<0?a+b+MOD:a+b;
    		}
    		template<typename T1,typename T2>constexpr auto mul(T1 a,T2 b) {
    			return (1ll*a*b%MOD+MOD)%MOD;
    		}
    		template<typename T,typename...Types>constexpr auto add(T a,Types... b) {
    			return add(a,add(b...));
    		}
    		template<typename T,typename...Types>constexpr auto mul(T a,Types... b) {
    			return mul(a,mul(b...));
    		}
    		int Pow(int a,int b) {
    			int res=1;
    			for(a%=MOD; b; b>>=1,Tomul(a,a))if(b&1)Tomul(res,a);
    			return res;
    		}
    		int _Pow(int n) {
    			int cnt=0;
    			for(; n; n/=MOD,cnt+=n);
    			return cnt;
    		}
    		int Inv(int x) {
    			return Pow(x,MOD-2);
    		}
    		int Fac(int n) {
    			int res=1;
    			for(; n>1; Tomul(res,fac[n%MOD]),n/=MOD)if((n/MOD)&1)res=MOD-res;
    			return res;
    		}
    		void Init() {
    			fac[0]=1;
    			FOR(i,1,MOD-1)fac[i]=mul(fac[i-1],i);
    		}
    		int Solve(int n,int m) {
    			return _Pow(n)-_Pow(m)>_Pow(n/m)*m?0:mul(Fac(n),Inv(Fac(m)),Inv(Pow(Fac(n/m),m)));
    		}
    	} M[5];
    	void Init() {
    		FOR(i,0,4) {
    			M[i].MOD=MOD[i];
    			if(i)M[i].Init();
    		}
    	}
    } using namespace Modular_Arithmetic;
    int Cas,n,m,ans,mod;
    int a[N];
    vector<int> Divide(int x) {
    	vector<int> vec;
    	vec.clear();
    	for(int i=1; i*i<=x; ++i)if(x%i==0) {
    			vec.push_back(i);
    			if(i*i!=x)vec.push_back(x/i);
    		}
    	return sort(vec.begin(),vec.end()),vec;
    }
    signed Cmain() {
    	cin>>n>>m,RCL(a,0,a,1);
    	vector<int> factors=Divide(n);
    	FOR(i,1,4)for(int &x:factors)toadd(M[i],a[i],M[i].Solve(n,x));
    	ans=a[4],mod=MOD[4];
    	DOR(i,3,1) {
    		while(ans%MOD[i]!=a[i])ans+=mod;
    		mod*=MOD[i];
    	}
    	cout<<M[0].Pow(m,ans)<<endl;
    	return 0;
    }
    signed main() {
    	for(Init(),cin>>Cas; Cas; --Cas)Cmain();
    	return 0;
    }
    

    后记

    这是一道数论好题,难得地集齐了四大数论定理,当然还可以用扩展卢卡斯定理做(但我不会),值得拿来练手,加深印象,其中对中国剩余定理的运用也是我先前所不知道的,这道题对数论能力真的非常有帮助。


    • 1

    信息

    ID
    1863
    时间
    1000ms
    内存
    256MiB
    难度
    9
    标签
    (无)
    递交数
    12
    已通过
    4
    上传者