题意:给出整数n,求1...n的排列中有那些数是13542或53124,即先上升后下降或先下降后上升,可以完全上升或下降。答案和p取模。
解法:找呀找呀找规律。从样例可以直接猜到规律就是2 ^ n - 2,然后我大概推理了一下。
当n等于3时,可以发现所有的排列都符合这一特点。
123, 132, 213, 231, 312, 321。
当新加入一个数字4时,对于完全上升或下降的排列有三个位置可以添加,3的两端和1的一端,例如123添加4后变成4123, 1243, 1234。对于先上升后下降的排列有两个位置可以添加,3的两端,例如132添加4后变成1432, 1342。对于先下降后上升的排列同样有两个位置可以添加,排列的两端,例如213添加4后变成4213, 2134。
所以对于n等于3时的答案a3来说,a4 = 2 * a3 + 2。得出数列ai = 2 * ai-1 + 2,求其通项公式得a1 = 1, an = 2 ^ n - 2。
由于n很大,使用快速幂计算。愉快的过了一判……二判TLE了,后来一看才知道因为p太大了还要用快速乘法这么个东西……原理和快速幂类似……orz
代码:
妈妈问我为什么跪着写代码系列
#include<stdio.h> #include<iostream> #include<algorithm> #include<string> #include<string.h> #include<math.h> #include<limits.h> #include<time.h> #include<stdlib.h> #include<map> #include<queue> #include<set> #include<stack> #include<vector> #define LL long long using namespace std; LL n, p; LL MUL(LL x, LL n)//快速乘法 { LL ret = 0; LL base = x; while(n) { if(n & 1) ret = (ret + base) % p; base = (base + base) % p; n >>= 1; } return ret; } LL POW(LL x, LL n)//快速幂 { LL base = x; LL ret = 1; while(n) { if(n & 1) ret = MUL(ret, base) % p; base = MUL(base, base) % p; n >>= 1; } return ret; } int main() { while(~scanf("%lld%lld", &n, &p)) { if(n == 1) printf("%lld ", ((LL)1 % p)); else { LL tmp = POW(2, n); printf("%lld ", (tmp - 2 + p) % p); } } return 0; }