|
问题描述
一年一度的ioiAKer大赛即将来临,何老板打算让信竞队的同学们组队参赛。信竞队共n名队员,他们的CF积分分别是1、2、3、...... 、n,CF积分体现了选手的能力水平,积分越高,能力越强。
按照赛会方的要求,每个学校只能派两只队伍参赛,分别对应提高组和提高组。为体现不同组别能力的区分度,要求提高组队伍中积分最低的选手必须大于普及组队伍中积分最高的选手。每组队伍的参赛人数不能为0。
何老板想知道,总共有多少种合法的组队方案。结果可能很大,mod m后再输出。
输入格式
一行,两个整数n和m
输出格式
一个整数,表示计算结果
样例输入
3 10
样例输出
5
提示
对于100%的数据:1<=n<=1018 1<=m<=1018
合法的组队方案如下: 提高组 普及组 {3,2} {1} {3} {1,2} {3} {2} {3} {1} {2} {1}
【题目分析】
设i在普及组中,且CF分数最高,显然,∑i=1n {2i - 1 * (2n - i - 1)} % m 就是答案。
即ans = ∑i=1n {2i - 1 * (2n - i - 1)} % m = ∑i=1n {2n - 1 - 2i - 1} % m = {∑i=1n 2n - 1 - ∑i=1n 2i - 1} % m
由等比数列求和公式,可化简为ans = (n * 2n - 1 - (2n - 1)) % m = (n * 2n - 1 - 2n + 1) % m,然后把模运算分配进括号里即可。
但是鉴于n范围过大,我们借用O(1)快速乘,此题解决。
以下安利O(1)快速乘模板:
ll Mul(ll a, ll b, ll c) { return (a * b - (ll)((long double)a / c * b) * c + c) % c; }
【参考代码】
1 #include<cstdio> 2 #define ll unsigned long long 3 ll n, m, ans, temp; 4 namespace Ironclad_Programming { 5 #define R register int 6 #define For(i, s, n) for (R i = s; i <= n; ++ i) 7 void ini() { 8 scanf("%lld%lld", &n, &m); 9 } 10 ll Mul(ll a, ll b, ll c) { 11 return (a * b - (ll)((long double)a / c * b) * c + c) % c; 12 } 13 ll Montgomery(ll a, ll b, ll c) { 14 ll ans = 1; 15 a %= c; 16 while (b) { 17 if ((b & 1))ans = Mul(ans % c, a % c, c); 18 b >>= 1; 19 a = Mul(a % c, a % c, c); 20 } 21 return ans; 22 } 23 void solve() { 24 temp = Montgomery(2, n - 1, m); 25 ans = Mul(n % m, temp % m, m); 26 temp = Montgomery(2, n, m); 27 ans = (ans - temp + 1 + m) % m; // 千万记得加一次模数,防负数 28 printf("%lld", ans); 29 } 30 void Main() { 31 ini(); 32 solve(); 33 } 34 #undef R 35 #undef For 36 } 37 int main() { 38 Ironclad_Programming::Main(); 39 return 0; 40 }