题意
给n个数字,从1到n,现在定义一种排列,满足一下两个条件
- 从{1-i}的排列是单调排列
- 从{i-n}的排列是单调排列
给定n和p求这种排列的个数模上p的结果
解法
- 如果i这个位置的数是不是1且不是n,那么只有从1-n的排列和从n到1的排列,有2种(其实包含在二和三中)
- 如果是1那么能形成的排列是1的两边都是单调递减,因为所有的数都大于1,每个数都可能在1的左边或者右边,总共有2的 2^(n-1) 种
- 同理,如果是n也是 2^(n-1) 种
- 然后发现第二种情况与第三种情况完全包含了第一种情况 , 同时第二种和第三种情况有完全重合的地方,也就是1在最左边那么n肯定就在最右边,同时n在最左边那么1肯定就在最右边,有二种是重复的
- 所以总的个数为 2^n - 2
注意
- 这个题目的n和p相当的大,有10^18
- 所以普通的快速幂是会爆的
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
using namespace std ;
typedef long long LL ;
LL Mod ;
LL mul(LL n , LL k) {
LL ans = 0 ;
n %= Mod ;
while(k) {
if(k&1) {
ans += n ;
if(ans > Mod) ans -= Mod ;//此处
}
n <<= 1 ;
if(n > Mod) n -= Mod ;//以及此处如果是去模运算700+ms ,减法则是70+ms
k >>= 1 ;
}
return ans ;
}
LL pow_mul(LL n , LL k) {
LL ans = 1 ;
while(k) {
if(k&1) ans = mul(ans , n) ;
n = mul(n , n) ;
k >>= 1 ;
}
return ans ;
}
int main() {
LL n ;
while(scanf("%I64d%I64d" , &n ,&Mod) == 2) {
if(n == 1) {
printf("%I64d\n" , n%Mod) ;
continue ;
}
printf("%I64d\n" , (pow_mul(2 , n)-2+Mod)%Mod) ;
}
}