• P2155 [SDOI2008]沙拉公主的困惑



    数学题都是屑题。

    题解

    题目要求 (sum_{i=1}^{n!}gcd(i,m!)=1) 这个式子。
    我们先想简单的,我们会求 (sum_{i=1}^{m!}gcd(i,m!)=1),即 (φ(m!))
    考虑到 (m<=n),所以 (n!) 一定是 (m!) 的倍数,所以我们可以将 (n!) 分成 (frac{n!}{m!})(m!),而每一段有 (φ(m!)) 个数与 (m!) 互质,所以总共有 (frac{n!}{m!}*φ(m!)) 个数与 (m!) 互质,答案就是 (frac{n!}{m!}*φ(m!))
    上面的推理依据于一个定理:若 (gcd(a,b)=1),则 (gcd(a+b*k,b)=1)
    证明:
    我们可以利用 (gcd) 的性质来证:(gcd(a,b)=gcd(b,a\%b)=gcd(a\%b,b)),那么我们反着推回去即可得到:(gcd(a,b)=gcd(a+b*k,b)),证毕。
    还有一个定理:若 (m=p_1^{q_1}p_2^{q_2}p_3^{q_3}...p_k^{q_k}(p_1,p_2,...,p_k)均为质数()),则 (m!) 的质因子仅有 (p_1,p_2,p_3...p_k)
    这样我们再对答案式子进行化简:
    (ans=frac{n!}{m!}*φ(m!)=frac{n!}{m!}*m!*frac{p_1-1}{p_1}*frac{p_2-1}{p_2}*...*frac{p_k-1}{p_k}=n!*prod_{i=1}^{k}frac{p_i-1}{p_i})
    然后我们可以预处理 (1e7) 范围内的 (n!)(prod_{i=1}^{k}frac{p_i-1}{p_i}),对于每次询问直接 (O(1)) 输出答案 。
    (jc[i]) 表示 (i!) 是多少。
    (inv[i]) 表示 (i) 的逆元是多少。
    (ans[i]) 表示当上面的 (m=i) 时,(prod_{i=1}^{k}frac{p_i-1}{p_i}) 是多少。
    求逆元的话需要线性的复杂度,这里再复习一下怎么求(主要是我也忘了(qwq)):
    我们设 (p=k*i+r(1<r<i<p))(k) 就是 (p/i) 的商,(r) 是余数。
    则有 (k*i+requiv0(mod) (p)),两边同乘 (i^{-1}*r^{-1}) 得:
    (k*r^{-1}+i^{-1}equiv0(mod) (p))
    (i^{-1}equiv-k*r^{-1}(mod) (p))
    (i^{-1}equiv-lfloor frac{p}{i} floor*(p) (mod) (i)^{-1} (mod) (p))

    inv[1] = 1;
    for(int i = 2; i < p; ++ i)
        inv[i] = (p - p / i) * inv[p % i] % p;
    

    对于 (ans[i]) 的维护,我们在枚举 (i) 的时候如果发现 (i) 是质数,就让 (ans[i]=ans[i-1]*frac{i-1}{i});否则的话 (ans[i]=ans[i-1])
    判断质数的过程我们可以用欧拉筛来实现。
    时间复杂度 (O(n)),稍微有点卡常(....)
    (Code:)

    #pragma GCC optimize(1)                //手动开O(3)卡常qwq         
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #pragma GCC optimize("inline")
    #pragma GCC optimize("-fgcse")
    #pragma GCC optimize("-fgcse-lm")
    #pragma GCC optimize("-fipa-sra")
    #pragma GCC optimize("-ftree-pre")
    #pragma GCC optimize("-ftree-vrp")
    #pragma GCC optimize("-fpeephole2")
    #pragma GCC optimize("-ffast-math")
    #pragma GCC optimize("-fsched-spec")
    #pragma GCC optimize("unroll-loops")
    #pragma GCC optimize("-falign-jumps")
    #pragma GCC optimize("-falign-loops")
    #pragma GCC optimize("-falign-labels")
    #pragma GCC optimize("-fdevirtualize")
    #pragma GCC optimize("-fcaller-saves")
    #pragma GCC optimize("-fcrossjumping")
    #pragma GCC optimize("-fthread-jumps")
    #pragma GCC optimize("-funroll-loops")
    #pragma GCC optimize("-freorder-blocks")
    #pragma GCC optimize("-fschedule-insns")
    #pragma GCC optimize("inline-functions")
    #pragma GCC optimize("-ftree-tail-merge")
    #pragma GCC optimize("-fschedule-insns2")
    #pragma GCC optimize("-fstrict-aliasing")
    #pragma GCC optimize("-falign-functions")
    #pragma GCC optimize("-fcse-follow-jumps")
    #pragma GCC optimize("-fsched-interblock")
    #pragma GCC optimize("-fpartial-inlining")
    #pragma GCC optimize("no-stack-protector")
    #pragma GCC optimize("-freorder-functions")
    #pragma GCC optimize("-findirect-inlining")
    #pragma GCC optimize("-fhoist-adjacent-loads")
    #pragma GCC optimize("-frerun-cse-after-loop")
    #pragma GCC optimize("inline-small-functions")
    #pragma GCC optimize("-finline-small-functions")
    #pragma GCC optimize("-ftree-switch-conversion")
    #pragma GCC optimize("-foptimize-sibling-calls")
    #pragma GCC optimize("-fexpensive-optimizations")
    #pragma GCC optimize("inline-functions-called-once")
    #pragma GCC optimize("-fdelete-null-pointer-checks")
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio> 
    #include<cmath>
    #include<stack>
    #define ll long long
    using namespace std;
    const ll N=1e7+5;
    int read()
    {
    	char ch=getchar();
    	int a=0,x=1;
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') x=-x;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		a=(a<<1)+(a<<3)+(ch^48);
    		ch=getchar();
    	}
    	return a*x;
    }
    int T,n,m,sum;
    ll M,jc[N],inv[N],ans[N],prime[N];
    bool is_prime[N];
    int main()
    {
    	T=read();M=(ll)read();
    	inv[0]=1;inv[1]=1;
    	ans[0]=1;ans[1]=1;
    	jc[0]=1;jc[1]=1;
    	for(int i=2;i<=1e7;i++)                //预处理1e7内的信息 
    	{
    		jc[i]=jc[i-1]*i%M;             //朴素算阶乘 
    		inv[i]=inv[M%i]*(M-M/i)%M;     //线性求逆元 
    		ans[i]=ans[i-1]%M;             //维护ans数组 
    		if(is_prime[i]==0)             //如果i是质数的话,要多乘一个(i-1)/i 
    		{
    			prime[++sum]=i;            
    			ans[i]=ans[i]%M*(i-1)%M*inv[i]%M;  //不能直接除以i,要乘i的逆元 
    		}
    		for(int j=1;j<=sum&&prime[j]*i<=1e7;j++)   //欧拉筛素数的过程 
    		{
    			is_prime[i*prime[j]]=1;
    			if(i%prime[j]==0) break;
    		}
    	}
    	while(T--)
    	{
    		n=read();m=read();
    		printf("%lld
    ",jc[n]%M*ans[m]%M);  //直接输出答案 
    	}
    	return 0;
    }
    
  • 相关阅读:
    SQLServer 可疑
    String与Long互转
    洛谷 P5644
    洛谷 P3783
    洛谷 P4663
    洛谷 P3438
    Atcoder Grand Contest 054 题解
    迭代器失效问题
    Solution -「CF 232E」Quick Tortoise
    Solution -「NOI 2020」「洛谷 P6776」超现实树
  • 原文地址:https://www.cnblogs.com/xcg123/p/13435723.html
Copyright © 2020-2023  润新知