P10380 「ALFR Round 1」D 小山的元力
/*
历时两天,算是搞出来了
先贴几个提醒
首先如果是用lucas定理并用阶乘形式来求组合数的,
请判断组合数是否成立,即`C(a, b)`,a是否大于等于b
如果小于你将re几个点
如果是直接用快速幂求解逆元来做的,恭喜你,你将WA#20和#46,
因为p可能小于n, m或n + m, 导致你求出的阶乘到了`f[p]`时
变为0,使得后面的计算错误
进入正题分析题意,首先对于每一堆,当这堆放k个元素的时候
无论这是哪一堆,它外面的情况总数都是一样的,也就是分配剩
下n - k个数的情况的数量。
堆与每一堆都如此,所以我们只需要求出一堆的ans = ai * 总情况数
在乘上sum = 1! + 2! + ... + m!即可
那么剩下的就是求总情况数,
设当前堆放k个数,那么就剩下n - k个数要分配到m - 1个空堆(可以不放)
这里可以用隔板法,用m - 2个隔板,把剩下n - k个数分成m - 1块
因为有空堆,而隔板法不能有空的分配,所以可以人为添加m - 1个
元素,把情况变成必须放,这时候元素有n - k + m - 1个,空隙(两边不算)
有n - k + m - 1 - 1个,即n - k + m - 2个
那么剩下的就是在这n - k - 2个空隙里面选m - 2个放上隔板
就是求C(n - k + m - 2, m - 2),
求组合数有很多方法,当时看数据范围,我直接用的快速幂求通过阶乘求解
然后因为p的大小寄掉了,也就是上面的提醒
因此这里用lucas定理求解组合数,这样就不会因为q的事情
寄掉了时间上也够
因为模数p不变,所以可以先预处理阶乘,但是不要先预处理,逆元
否则时间复杂度会变成O(plogn)容易TLE
动态求解逆元,求出组合数。
最后把组合数乘上ai然后全部相加 = ans
最后输出ans * sum % p即可
注意当m == 1的时候需要特判
*/
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 11000100;
int n, m, p;
int f[N];
int sum;
int qmi(int a, int k, int p)
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
k >>= 1;
a = (LL)a * a % p;
}
return res;
}
int in_f(int x) // 逆元
{
return qmi(x, p - 2, p);
}
int C(int a, int b)
{
if (a < b) return 0; // 一定要判断,不然会re,可能本地没问题,但那只是越界不够大而已
return (LL)f[a] * in_f(f[b]) % p * in_f(f[a - b]) % p;
}
int lucas(int a, int b) // 获取C(a, b)组合数
{
if (a < p && b < p) return C(a, b);
return (LL)C(a % p, b % p) * lucas(a / p, b / p) % p;
}
int main()
{
cin >> n >> m >> p;
if (m == 1) // 特判
{
cout << n % p;
return 0;
}
f[0] = 1;
int maxv = max(m, p - 1);
for (int i = 1; i <= maxv; i ++ )
{
f[i] = (LL)f[i - 1] * i % p;
if (i <= m) sum = (sum + f[i]) % p;
}
int ans = 0;
for (int i = 0; i <= n; i ++ )
{
ans = (ans + (LL)i * lucas(n - i + m - 2, m - 2) % p) % p;
}
cout << (LL)ans * sum % p << endl;
return 0;
}
爆了的单用快速幂的算法
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 2100010;
int n, m, p;
int f[N], in_f[N];
int sum;
int qmi(int a, int k, int p)
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
k >>= 1;
a = (LL)a * a % p;
}
return res;
}
int get(int a, int b)
{
return (LL)f[a] * in_f[b] % p * in_f[a - b] % p;
}
signed main()
{
cin >> n >> m >> p;
if (m == 1) // 特判
{
cout << n % p;
return 0;
}
f[0] = in_f[0] = 1;
for (int i = 1; i <= n + m + 100; i ++ )
{
f[i] = (LL)f[i - 1] * i % p;
in_f[i] = qmi(f[i], p - 2, p) % p;
if (i <= m) sum = (sum + f[i]) % p;
}
if (n == 1)
{
cout << sum % p << endl;
return 0;
}
int ans = 0;
for (int i = 0; i <= n; i ++ )
{
ans = (LL)(ans + (LL)i * get(n - i + m - 2, m - 2) % p) % p;
}
cout << (LL)ans * sum % p << endl;
// cout << get(3, 0);
return 0;
}