HDU 5187 思维+快速幂模板

来源:互联网 发布:大宗交易数据查询 编辑:程序博客网 时间:2024/05/22 03:43

题目链接

题意

  • 给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) ;    }}
0 0
原创粉丝点击