就是以那个ai为分水岭,左边和右边都分别是单调增或单调减如图
就这四种情况,其中头两种总共就是两个序列,也就是从头到尾递增和从头到尾递减。
后两种方式就是把序列中德数分为左右两派,分完以后左右两边各自内部的排法就已经确定了,至于ai早就确定了(不是全局最大就是全局最小),而除了ai的每一个数都有选择在左或是在右两种选择,所以是2^(n-1),总共就是2^n,而这里包括了前两种的方案,所以要-4,最终应有2^n-2种。
看数据范围就知道要用快速幂,不过可惜如果只用快速幂会错,应为n和p的范围都是10^18,快速幂里在还没有模p之前的乘法都有可能超出long long int。所以想到用加法,a*b就是b个a相加,每一步都模p,8个a相加 =c c+c = 16个a 相加, 和快速幂一个道理。 然后用快速幂的原理不断二分,这就是所谓的快速乘法。
还有就是特殊数据,当n输入为1的时候情况数不是0而是1,当然不像BC上说的那样结果就是1,而是1MODp。
1 // 2 // main.cpp 3 // hdu5187 4 // 5 // Created by opas on 15/3/18. 6 // Copyright (c) 2015年 opas. All rights reserved. 7 // 8 9 #include <iostream> 10 #include <cstdio> 11 #include <string.h> 12 using namespace std; 13 typedef long long LL; 14 LL modj(LL a, LL b, LL m){ 15 LL ans=0; 16 while(b){ 17 if(b&1) ans=(ans+a)%m; 18 b>>=1; 19 a=(a+a)%m; 20 } 21 return ans; 22 } 23 LL modx(LL a, LL b, LL m){ 24 LL ans=1; 25 while(b){ 26 if(b&1)ans=modj(ans,a,m); 27 b>>=1; 28 a = modj(a,a,m); 29 } 30 return ans; 31 } 32 int main(int argc, const char * argv[]) { 33 LL n,p; 34 while(scanf("%lld%lld",&n,&p)==2){ 35 if(n==1){ 36 printf("%d\n",p>1?1:0); continue; 37 } 38 printf("%lld\n",( modx( 2, n, p ) -2+p)%p ); 39 } 40 return 0; 41 }