[练习][HDU5187]快速幂加强(快速幂+快速乘) zhx's contest

题目背景
HDU5187

题目描述
As one of the most powerful brushes, zhx is required to give his juniors n problems.
zhx thinks the ith problem’s difficulty is i . He wants to arrange these problems in a beautiful way.
zhx defines a sequence {ai} beautiful if there is an i that matches two rules below:
1:a1..ai are monotone decreasing or monotone increasing.
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≤1018

输出格式
对于每组测试数据,答案输出一行。

样例数据
输入

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个数可供选择) C0n1+C1n1+C2n1+..+Cn1n1 ,根据公式可知和是等于 2n1 的,由于1、n开口方向情况是不一样的,所以要*2,而 C0n1 Cn1n1 比较特殊,如果*2相当于就计算了两次整个排列单调递增和单调递减,所以还要-2,故答案为 2n2 (注意特判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;
}

本题结。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值