题目背景
HDU5187
题目描述
As one of the most powerful brushes, zhx is required to give his juniors
n
problems.
zhx thinks the
zhx defines a sequence {
1:
2:
ai..an
are monotone decreasing or monotone increasing.
He wants you to tell him that how many permutations of problems are there if the sequence of the problems’ difficulty is beautiful.
zhx knows that the answer may be very huge, and you only need to tell him the answer module
p
.
题目大意
给你1—n这n个数,让你求有多少种排列能找到一个数i,他的左边是单调的右边也是单调的(答案可能很大,对p取模)。
输入格式
多组测试数据(小于1000)。将EOF作为文件的结尾。
对于每组测试数据,一行两个整数n和p由一个空格隔开。(1≤N、P≤
输出格式
对于每组测试数据,答案输出一行。
样例数据
输入
2 233
3 5
输出
2
1
备注
【样例说明】
在第一种情况下,序列{ 1, 2 }和{ 2, 1 }都是合法的。
在第二种情况下,序列{ 1, 2, 3 }、{ 1, 3, 2 }、{ 2, 1, 3 }、{ 2, 3, 1 }、{ 3, 1, 2 }、{ 3, 2, 1 }是合法的,所以答案是6 mod 5=1。
分析:乍眼一看看不出来,然后慢慢分析发现,我们只用看1和n。对于1,左侧单调递减右侧单调递增;对于n,左侧单调递增右侧单调递减,所以我们只需要枚举1(或n)的左侧有多少个数(他们的排列是确定的,而右侧就是剩下的数,排列也是确定的),然后排列组合求有多少种选数组合(现在有n-1个数可供选择) C0n−1+C1n−1+C2n−1+..+Cn−1n−1 ,根据公式可知和是等于 2n−1 的,由于1、n开口方向情况是不一样的,所以要*2,而 C0n−1 和 Cn−1n−1 比较特殊,如果*2相当于就计算了两次整个排列单调递增和单调递减,所以还要-2,故答案为 2n−2 (注意特判n=1时,答案不是0是1,但是好像HDU没有卡这个)。根据数据范围,该用快速幂加强防止爆long long。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;
long long getlong()
{
long long sum=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
long long n,p;
long long ksc(long long x,long long y)
{
long long res=0;
for(;y>=1;y=y>>1,x=(x+x)%p)
if(y&1)
res=(res+x)%p;
return res;
}
void ksm()
{
long long ans=1,m=2;
if(n==1)//特判n=1
ans=3;
else
for(;n>=1;n=n>>1,m=ksc(m,m))
if(n&1)
ans=ksc(ans,m);
ans=(ans+p-2)%p;//在有取模的减法中要加一个模数,因为可能原数被模之后比被减数还小,造成答案变成负数
cout<<ans<<'\n';
}
int main()
{
freopen("zhx.in","r",stdin);
freopen("zhx.out","w",stdout);
while(scanf("%I64d",&n)!=EOF)
{
p=getlong();
ksm();
}
return 0;
}
本题结。