就是以那个ai为分水岭,左边和右边都分别是单调增或单调减如图

hdu 5187 快速幂 + 快速乘 值得学习_编程

        就这四种情况,其中头两种总共就是两个序列,也就是从头到尾递增和从头到尾递减。

        后两种方式就是把序列中德数分为左右两派,分完以后左右两边各自内部的排法就已经确定了,至于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 }