初见安~这里是传送门:洛谷P2480 古代猪文【其实这个题咕了好久了才写……
题解
【本题有种数论知识点大杂烩的感觉,若有知识点残缺欢迎来走走:定理传送门,Lucas传送门】
数论题啊……一眼下来连题都读不懂呢。样例知道怎么来的了就很好整了。
说白了题意就是求。
n的范围是1e9,所以对于因数d的枚举不会超过,可以暴力找;但是求组合数就爆掉了。因为模数很大,n也很大,你预处理fac会爆空间,直接Lucas作用又不大……所以我们就要把模数降下来。
可以想到欧拉定理——,且a和n互质。//这里搁浅一下,文末继续
因为999911659是质数,所以。
所以问题就可以转化为求:
对于把模数降下来,我们引入一个定理::
对于两个整数x,y,如果同余一个数p,那么同样同余这个数的所有因数。
例如,则一定有。
证明显然【严谨公式证明有点儿困难,就口胡了……】:
- 设,则有;
- 设x是p的某个因数,则必有;
- 所以a和b都只剩下t,同一个数取余同一个数等于同一个数;
- 证毕。
所以我们设,则ans与这一坨sigma同余。
所以可以转化到:
也就是说我们最后要求的ans就是上面这个方程式的最小正整数解了。对于我们可以暴力求组合数,因为模数已经很小了所以方法很多。再然后用中国剩余定理(CRT)求解即可。
到这里其实这个题就差不多了。但是最后的最后你可能只有95分,WA 第13个点。为什么??!
我们回到前面搁浅的那个问题上来吧。因为欧拉定理的互质条件,所以我们可以特判一下:如果不互质,直接输出0,或者写一个扩展欧拉定理——求,当a和n不互质并且的时候,有。这里就顺便解释一下代码中特别注释的做法:【直接选择特判的可以跳过】
因为在欧拉定理下,有三种情况:
- a、n互质,
- a、n不互质,并且,
- a、n不互质,并且,直接暴力求即可。
也就是说,除了第三种情况,我们都可以直接用【因为】。而很巧的是,对于这个题,因为999911659是个质数,所以最后的指数在999911659以内时两者互质,都是第一种情况,可以直接套用;而与它不互质的时候,指数早就大于1e9了,一定会被mod下来,所以只存在第一种和第二种情况,我们输出答案的时候直接套这个公式是可行的呢!!!【当然一般情况下最好不要这么冒险】
最后的最后【其实您已经可以不用看了】,本狸因为tcl所以在求组合数这个问题上纠结了很久……因为我Lucas的那篇文章的求法并不适用了,但是用预处理fac和fac的inv的方法也不行,inv从后往前推过来全是0【QuQ我也不知道为什么】,所以只能用费马小,mod-2次方来求逆元……
说了这么多,也可以上代码了——
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 40005
using namespace std;
typedef long long ll;
ll read() {
ll x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
ll mod[5] = {999911659, 2, 3, 4679, 35617}, a[5];
ll fac[maxn];
ll pw(ll a, ll b, ll p) {ll res = 1; a %= p; while(b) {if(b & 1) res = res * a % p; a = a * a % p, b >>= 1;} return res;}
ll C(ll n, ll m, ll p) {
if(n < m) return 0; if(!m) return 1;
return fac[n] * pw(fac[m], p - 2, p) % p * pw(fac[n - m], p - 2, p) % p;
}
ll Lucas(ll n, ll m, ll p) {if(n < m) return 0; return m? C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p : 1;}
ll ans = 0;
void CRT() {//中国剩余定理
ll M = mod[0] - 1, x, y;//这里M是\phi(n_,所以要-1
for(int i = 1; i <= 4; i++)
ans = (ans + M / mod[i] * a[i] % M * pw(M / mod[i], mod[i] - 2, mod[i]) % M) % M;
}
signed main() {
ll n, G;
n = read(), G = read();
if(G % mod[0] == 0) {puts("0"); return 0;}//这里是直接特判的版本
for(int t = 1; t <= 4; t++) {
fac[0] = fac[1] = 1;
for(ll i = 2; i <= 40000; i++) fac[i] = fac[i - 1] * i % mod[t];//这里写的是4e4,其实卡着mod写也行
for(ll i = 1; i * i <= n; i++) if(n % i == 0) {
a[t] = (a[t] + Lucas(n, i, mod[t])) % mod[t];//暴力求和
if(i * i != n) a[t] = (a[t] + Lucas(n, n / i, mod[t])) % mod[t];
}
}
CRT();
// printf("%lld\n", pw(G, ans + mod[0] - 1, mod[0])); 这就是直接套用欧拉公式的版本
printf("%lld\n", pw(G, ans, mod[0]));
return 0;
}
真的写了好久……哎。
迎评:)
——End——