【数论】contest
分析:首先题目的含义是,在 n n n 个位置上分别放入 1 ∼ n 1 \sim n 1∼n 这 n n n 个数,使得在 某一个点 之前,序列满足单调递增或单调递减,在这个点之后,序列满足单调递增或单调递减。
实际上我们可以枚举 每一个位置 x x x 作为 断点,分成两种情况讨论:
1. 对于点 x x x 而言,它的 左侧 是 单调递增 的,右侧单调递减。
2. 对于点 x x x 而言,它的 右侧 是 单调递减的, 左侧单调递增。
那么有一个问题是,当 整个 序列 都是 单调递增 或 单调递减时,我们上述的方法会考虑到吗?
再次考虑一下,当 x = 1 x = 1 x=1 时,情况 1 1 1 能考虑到 整个序列都是单调递减 的情况。 情况 2 2 2 则能考虑到 整个序列都是单调递增 的情况。
而当 x = n x = n x=n 时,同样也会考虑到这两种情况,所以 这两种特殊情况不仅被考虑到了,而且分别被多考虑了一次,所以应该将多余答案 减去。
因为 断点不同,所以每种方案数都是不一样的,所以我们可以列出等式:
r e s = ( ∑ i = 1 n g ( i ) + f ( i ) ) − 2 res = (\sum_{i = 1}^{n} g(i) + f(i)) - 2 res=(∑i=1ng(i)+f(i))−2 (其中 g ( i ) g(i) g(i) 和 f ( i ) f(i) f(i) 分别表示以 i i i 位置为断点,情况 1 1 1 和 情况 2 2 2 的方案数)。
那么我们在考虑一下 g ( i ) g(i) g(i) 和 f ( i ) f(i) f(i) 怎么算:
对于 g ( x ) g(x) g(x) 而言,那么显然, x x x 这个位置必须要放上 n n n 这个数,因为在整个序列中它是最大的。而在它放过 n n n 后,剩余 n − 1 n - 1 n−1 个数都是比它小的 ,我们只需要从中 任意 选出来 x − 1 x - 1 x−1 个数字,通过一定的安排顺序,就可以满足位置 x x x 的前半部分, 而当我们选出来 x − 1 x - 1 x−1 放在 x x x 位置的前面时, x x x 位置的前半部分 和 后半部分的数字摆放顺序其实已经确定。(因为 n n n 个数字 不一样 且要按严格的 递增 或 递减 顺序摆放),所以 g ( x ) = C n − 1 x − 1 g(x) = C_{n - 1}^{x - 1} g(x)=Cn−1x−1
而求解 f ( x ) f(x) f(x) 的过程实际与求解 g ( x ) g(x) g(x) 的思路 一摸一样,只不过将 x x x 位置上的数换成 最小数 1 1 1,而答案实际也是 f ( x ) = C n − 1 x − 1 f(x) = C_{n-1}^{x - 1} f(x)=Cn−1x−1
所以问题的答案也就变成了 r e s = 2 ∗ ∑ i = 1 n C n − 1 i − 1 − 2 res = 2 * \sum_{i = 1}^{n} C_{n - 1}^{i - 1} - 2 res=2∗∑i=1nCn−1i−1−2。
这个式子该如何化简呢?我们将式子展开写试试:
r e s = 2 ∗ ( C n − 1 0 + C n − 1 1 + C n − 1 2 + C n − 1 3 + . . . + C n − 1 n − 1 ) − 2 res = 2 * (C_{n - 1}^{0} + C_{n - 1}^{1} + C_{n - 1}^{2} + C_{n - 1}^{3} + ... + C_{n - 1}^{n - 1}) - 2 res=2∗(Cn−10+Cn−11+Cn−12+Cn−13+...+Cn−1n−1)−2。
我们发现和 二项式定理公式 很相似:
( a + b ) n = C n 0 a 0 b n + C n 1 a 1 b n − 1 + C n 2 a 2 b n − 2 + . . . + C n n a n b 0 (a + b)^n = C_{n}^{0} a^0b^n +C_{n}^{1}a^1b^{n - 1} +C_{n}^{2}a^2b^{n - 2} +... + C_{n}^{n}a^nb^0 (a+b)n=Cn0a0bn+Cn1a1bn−1+Cn2a2bn−2+...+Cnnanb0
然后我们 令 a = b = 1 a = b = 1 a=b=1 ,则有:
怎么样,是不是很 NB ,我们再次将思路回到原题:
那么答案就是 r e s = 2 ∗ 2 n − 1 − 2 res = 2 * 2^{n - 1} - 2 res=2∗2n−1−2
= 2 n − 2 = 2^n - 2 =2n−2
但是考虑到 模数很大,用快速幂直接乘会爆 l o n g l o n g long long longlong,所以我们再加一个 龟速乘。这样就能愉快的 AC 了!!
CODE:
#include //答案 2^n - 2
using namespace std;
typedef long long LL;
LL n, p;
LL mul(LL x, LL y, LL mod){//龟速乘
LL res = 0;
while(y){
if(y & 1) res = (res + x) % mod;
x = (x + x) % mod;
y >>= 1;
}
return res;
}
LL q_pow(LL x, LL y, LL mod){//快速幂
LL res = 1;
while(y){
if(y & 1) res = mul(res, x, mod) % mod;
x = mul(x, x, mod) % mod;
y >>= 1;
}
return res;
}
int main(){
freopen("contest.in", "r", stdin);
freopen("contest.out", "w", stdout);
while(~scanf("%lld%lld", &n, &p)){
printf("%lld\n", (((q_pow(2, n, p) - 2) % p) + p) % p);
}
return 0;
}