hdu 5187-zhx's contest

本文介绍了一种计算特定组合序列数量的方法,对于给定整数n,找出所有满足特定单调递增或递减条件的序列数量。通过划分四种基本序列类型,并分析新增元素对各类型的影响,最终利用矩阵快速幂实现高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目及代码:大意就是给定一个数n,问使用数字1到n能组合出多少组同时满足下列两种情况的序列:

                    1.序列中的数字从第一项到第i项(1≤i≤n),满足单调递增或者递减;

                     2.序列中的数字从第i项到第n项满足单调递增或者递减;

    官方题解给的是2^n-2(n≥2),n=1时为1。推到方法不明......

    这里说下我的做法:首先将序列分为4种情况:1.单调递增的(A);2.单调递减的(B);3.单调递减后点掉递增的(C);4.单调递增后单调递减的(D)。

    然后我们在找一下它们之间的关系,就以3推4为例:1.单调递增的:1 2 3,我们要把4加进来,只有三种情况4 1 2 3,1 2 4 3和1 2 3 4,我们看出第一个属于第三种情况,第二个属于第四种情况,最后一个属于第一种情况,也就是说由于4的进入第三种情况的序列增加了1,第4中情况的序列增加了1,而第一种保持不变。

    2.单调递减的:3 2 1,与第一种情况相似,4 3 2 1,3 4 2 1和3 2 1 4,第三种情况的序列增加了1,第4中情况的序列增加了1,而第二种保持不变。

    3.单调递减后点掉递增的:这样的序列如2 1 3,我们只能把4放在左边或者右边即4 2 1 3和2 1 3 4,即第三种情况变成了原来的2倍。

    4.单调递增后单调递减的:如1 3 2,这样我们只能把4放在中间,而又分为两种情况,1 3 4 2和1 4 3 2,即第四种情况变成了原来的2倍。

    综合下来,我们就能看出增加有一个数字其效果是:A=A‘=1,B=B’=1,C=2*C+2,D=2*D+2。

    公式出来了,然后直接写矩阵直接矩阵快速幂加速求解就可以了。

    但是这里会遇到一个问题,就是mod太大了,矩阵乘法会爆精度,这里要注意把乘法转化成加法来计算。


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
typedef unsigned long long ll;

//乘法换成加法
ll multi(ll a,ll b,ll p)
{
    ll ret=0;
    while(b>0)
    {
        if(b&1)
        {
            ret=(ret+a)%p;
        }
        b>>=1;
        a=(a<<1)%p;
    }
    return ret;
}


struct mat
{
    ll t[3][3];
    void set()
    {
        memset(t,0,sizeof(t));
    }
}a,b;

mat multiple(mat a,mat b,ll n,ll p)
{
    ll i,j,k;
    mat temp;
    temp.set();
    for(i=0; i<n; i++)
        for(j=0; j<n; j++)
        {
            if(a.t[i][j]!=0)
                for(k=0; k<n; k++)
                    temp.t[i][k]=(temp.t[i][k]+multi(a.t[i][j],b.t[j][k],p))%p;
        }
    return temp;
}

mat quick_mod(mat b,ll n,ll m,ll p)
{
    mat t;
    t.set();
    for(ll i=0; i<n; i++) t.t[i][i]=1;
    while(m)
    {
        if(m&1)
        {
            t=multiple(t,b,n,p);
        }
        m>>=1;
        b=multiple(b,b,n,p);
    }
    return t;
}

void init()
{
    b.set();
    b.t[0][0]=2;
    b.t[1][1]=2;
    b.t[2][0]=2;
    b.t[2][1]=2;
    b.t[2][2]=1;
}

int main()
{
    ll n,k;
    while(scanf("%I64d%I64d",&n,&k)!=EOF)
    {
        if(n==1)
        {
            printf("%I64d\n",1%k);
            continue;
        }
        if(n==2)
        {
            printf("%I64d\n",2%k);
            continue;
        }
        if(n==3)
        {
            printf("%I64d\n",6%k);
            continue;
        }
        init();
        a=quick_mod(b,3,n-3,k);
        printf("%I64d\n",(2*((1+a.t[0][0]+a.t[0][1]+a.t[1][0]+a.t[1][1])%k)%k+a.t[2][0]+a.t[2][1])%k);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值