HDU 4602 Partition (矩阵快速幂或求通项)

Partition

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3462 Accepted Submission(s): 1348

Problem Description
Define f(n) as the number of ways to perform n in format of the sum of some positive integers. For instance, when n=4, we have
4=1+1+1+1
4=1+1+2
4=1+2+1
4=2+1+1
4=1+3
4=2+2
4=3+1
4=4
totally 8 ways. Actually, we will have f(n)=2(n-1) after observations.
Given a pair of integers n and k, your task is to figure out how many times that the integer k occurs in such 2(n-1) ways. In the example above, number 1 occurs for 12 times, while number 4 only occurs once.

Input
The first line contains a single integer T(1≤T≤10000), indicating the number of test cases.
Each test case contains two integers n and k(1≤n,k≤109).

Output
Output the required answer modulo 109+7 for each test case, one per line.

Sample Input
2
4 2
5 5

Sample Output
5
1

题意

对于一个数n可以拆成题意所给的形式,给你n和k问你k在n所拆成的形式中出现了几次

思路

我们先写一写1 2 3 4 5中各项的值
1 1
2 1 2
3 1 2 5
4 1 2 5 12
5 1 2 5 12 28
写到5我们其实就可以发现值的出现是具有一定规律的,5中有1个5,2个4,5个3,12个2,28个1
然后我们再观察一下这些数会很容易发现:
2=21+0
5=2
2+1
12=25+2
28=2
12+4(多写一步6中有几个1,会发现是64个)
64=2*28+8
也就是说这些式子从2开始满足:
a n = 2 a n − 1 + 2 n − 2 a_{n}=2a_{n-1}+2^{n-2} an=2an1+2n2 a 1 = 2 , a 2 = 5 a_{1}=2,a_{2}=5 a1=2,a2=5, n ≥ 2 n\geq 2 n2
原题的n和k可以化成n=n-k
对于 a n = 2 a n − 1 + 2 n − 2 a_{n}=2a_{n-1}+2^{n-2} an=2an1+2n2我们可以用矩阵快速幂的形式来求解
所构造的矩阵如下
这里写图片描述

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 3
using namespace std;
typedef struct
{
    long long m[MAX][MAX];
} Matrix;
Matrix P= {2,0,2,0,2,1,0,0,2};
Matrix I= {1,0,0,0,1,0,0,0,1};
const long long mod=1e9+7;
Matrix Matrixmul(Matrix a,Matrix b)
{
    int i,j,k;
    Matrix c;
    for(i=0; i<MAX; i++)
        for(j=0; j<MAX; j++)
        {
            c.m[i][j]=0;
            for(k=0; k<MAX; k++)
            {
                c.m[i][j]+=(a.m[i][k]*b.m[k][j])%mod;
            }
            c.m[i][j]%=mod;
        }
    return c;
}
Matrix quickpow(long long n)
{
    Matrix m=P,b=I;
    while(n>0)
    {
        if(n%2==1)
            b=Matrixmul(b,m);
        n=n/2;
        m=Matrixmul(m,m);
    }
    return b;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        int m=n-k;
        if(m<0)
        {
            printf("0\n");
        }
        else if(m==0)
        {
            printf("1\n");
        }
        else if(m==1)
        {
            printf("2\n");
        }
        else if(m==2)
        {
            printf("5\n");
        }
        else
        {
            Matrix A=quickpow(m-2);
            printf("%lld\n",(A.m[0][0]*5+A.m[0][1]*2+A.m[0][2]*1)%mod);
        }
    }
    return 0;
}

然后说说怎么求解通项
a n − 2 a n − 1 = 2 n − 2 a_{n}-2a_{n-1}=2^{n-2} an2an1=2n2可以利用线性非齐次递推关系的方法来求解也可以用构造等差数列的方式求解,先说线性非齐次递推关系的方法

  • 线性非齐次递推关系
    a n − 2 a n − 1 = 2 n − 2 a_{n}-2a_{n-1}=2^{n-2} an2an1=2n2可得到特征方程
    x 2 − 2 x = 0 x^2-2x=0 x22x=0,解得x=2,x=0
    特征根为一重根
    所以特解 ∗ a n = k n 2 n − 2 \ast a_{n}=kn2^{n-2} an=kn2n2,代入原式得k=1
    那么有 a n = ( A n + B ) 2 n + n 2 n − 2 a_{n}=(An+B)2^{n}+n2^{n-2} an=(An+B)2n+n2n2,再代入 a 2 = 5 , a 3 = 12 a_{2}=5,a_{3}=12 a2=5,a3=12
    A = 0 , B = 3 4 A=0,B=\frac{3}{4} A=0B=43
    那么有 a n = ( 3 + n ) 2 n − 2 a_{n}=(3+n)2^{n-2} an=(3+n)2n2, n ≥ 2 n\geq 2 n2

  • 构造等差数列
    a n − 2 a n = 2 n − 2 a_{n}-2a_{n}=2^{n-2} an2an=2n2两边同时除以 2 n 2^{n} 2n

a n 2 n − a n − 1 2 n − 1 = 1 4 \frac{a_{n}}{2^{n}}-\frac{a_{n-1}}{2^{n-1}}=\frac{1}{4} 2nan2n1an1=41
所以有
a n 2 n = 5 4 + ( n − 2 ) 1 4 \frac{a_{n}}{2^{n}}=\frac{5}{4}+(n-2)\frac{1}{4} 2nan=45+(n2)41
那么
a n = 5 4 2 n + ( n − 2 ) 1 4 2 n a_{n}=\frac{5}{4}2^{n}+(n-2)\frac{1}{4}2^{n} an=452n+(n2)412n

a n = ( 3 + n ) 2 n − 2 a_{n}=(3+n)2^{n-2} an=(3+n)2n2, n ≥ 2 n\geq 2 n2

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
const long long mod=1e9+7;
long long quickmod(long long n)
{
    long long ans=1;
    long long a=2;
    while(n>0)
    {
        if(n%2==1)
            ans=(ans*a)%mod;
        a=a*a%mod;
        n/=2;
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        long long n,k;
        scanf("%lld%lld",&n,&k);
        if(n-k<0)
            printf("0\n");
        else if(n-k==0)
            printf("1\n");
        else if(n-k==1)
            printf("2\n");
        else
        {
            n=n-k;
            printf("%lld\n",(n+3)*quickmod(n-2)%mod);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值