bzoj2186
欧拉函数+逆元
并没有什么想法
gcd有一个性质,gcd(i,j)=gcd(i+j,j),这个性质有什么用呢?我们发现phi(m!)找出了在m!以内所有和m互质的数,但是我们没有找到在n!范围内的数,但是根据刚才的性质,gcd(i,j)=gcd(i+j,j),那么gcd(i,m!)=1,gcd(i+m!,m!)=1,所以每个m!以内和m!互质的数都可以通过这个方法拓展,那么对于每个i,算上自己一共可以拓展n!/m!次,那么答案就是phi(m!)*n!/m!
根据欧拉函数的求法,phi(m!)=m!*π(pi-1)/pi,pi是m!的所有质因子,其实就是m以内所有质数,那么答案就是phi(m!)*n!/m!=π(pi-1)/pi*n!,这肯定是一个整数,然后就线性筛出pi,阶乘和逆元预处理出来就可以O(1)查询了。
线性求逆元貌似炸了,谁能帮忙看一下
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<vector> using namespace std; typedef long long ll; const int N = 10000010; int T; ll n, m, p; ll ans[N], fac[N], inv[N]; int pri[500050]; bool mark[N]; void ini() { ans[0] = ans[1] = fac[0] = fac[1] = 1; for(int i = 2; i <= 10000000; ++i) { fac[i] = fac[i - 1] * (ll)i % p; if(!mark[i]) pri[++pri[0]] = i; ans[i] = 1; for(int j = 1; j <= pri[0] && i * pri[j] <= 10000000; ++j) { mark[i * pri[j]] = 1; if(i % pri[j] == 0) break; } } inv[0] = inv[1] = 1; for(int i = 2; i <= 10000000 && i < p; ++i) inv[i] = (ll)(p - p / i * inv[p % i]) % p; for(int i = 2; i <= 10000000; ++i) { if(!mark[i]) ans[i] = ans[i - 1] * (ll)(i - 1) % p * inv[i % p] % p; else ans[i] = ans[i - 1]; } } int main() { cin >> T >> p; ini(); for(; T; --T) { scanf("%lld%lld", &n, &m); printf("%lld\n", ans[m] % p * fac[n] % p); } return 0; }
exgcd求逆元
exgcd求逆元,我们求a关于p的逆元x,那么就是a*x=1(mod p),这不就是一个同余方程吗?那么可以转化为ax+py=1,求x的整数解,因为有逆元当且仅当gcd(a,p)==1,所以这个可以直接exgcd,求出x的最小正整数解,最小正整数解是(x%t+t)%t,t=p/gcd(a,p),因为gcd(a,p)==1,所以就是p了
具体可以看这里
http://www.cnblogs.com/19992147orz/p/7337301.html
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<vector> using namespace std; typedef long long ll; const int N = 10000010; int T; ll n, m, p; ll ans[N], fac[N]; int mark[N], pri[N]; void exgcd(ll a, ll b, ll &x, ll &y) { if(b == 0) { x = 1; y = 0; return; } exgcd(b, a % b, y, x); y -= (a / b) * x; } ll inv(ll n) { ll x, y; // printf("n = %lld p = %lld\n", n, p); exgcd(n, p, x, y); return (x % p + p) % p; } void ini() { ans[0] = ans[1] = fac[0] = fac[1] = 1; for(int i = 2; i <= 10000000; ++i) { fac[i] = fac[i - 1] * (ll)i % p; if(!mark[i]) pri[++pri[0]] = i; ans[i] = 1; for(int j = 1; j <= pri[0] && i * pri[j] <= 10000000; ++j) { mark[i * pri[j]] = 1; if(i % pri[j] == 0) break; } } for(int i = 2; i <= 10000000; ++i) { if(!mark[i]) ans[i] = ans[i - 1] * (ll)(i - 1) % p * inv(i) % p; else ans[i] = ans[i - 1]; } } int main() { cin >> T >> p; ini(); for(; T; --T) { scanf("%lld%lld", &n, &m); printf("%lld\n", ans[m] % p * fac[n] % p); } return 0; }
发现了一些奇怪的问题,hzwer和popoqqq的程序样例都过不去。。。但是在bzoj上能过。。。线性求逆元是不是有bug啊。。。