foj2198 Problem 2198 快来快来数一数 dp 矩阵快速幂

32 篇文章 0 订阅
24 篇文章 0 订阅

Problem 2198 快来快来数一数
Accept: 67 Submit: 194
Time Limit: 1000 mSec Memory Limit : 65536 KB
Problem Description
n个六边形排成一行,相邻两个六边形共用一条边,如下图所示:

记这个图形的生成树个数为t(n)(由于每条边都是不同的,不存在同构的问题)。那么t(1)=6,t(2)=35……

给出n,求mod 1000000007

Input
第一行给出数据组数T。

之后每一行给出一个正整数n。

T大约为50000,n<=10^18。

Output
每组数据输出一行答案。

Sample Input
2
2
12345678987654321
Sample Output
41
733521876
Source
FOJ有奖月赛-2015年10月

Submit Back Status Discuss
这里写图片描述
用dp[i][0]表示,有i个六边形,没有最右边的个数。
dp[i][1]表示,有i个六边形,有最右边的个数。
dp[i][2]表示,前i个,总的方案数。答案就是dp[n][2];
i个六边形总个数就是dp[i][0] + dp[i][1];
dp[i+1][0] = dp[i][0] + dp[i][1];上图中1 2情况,新加的六边形,只有一种方法。
dp[i+1][1] = dp[i][0] * 4 + dp[i][1] * 5;上图中3 4 5情况,新加的图形有4种方式去边,还有一种方法,就是把最右边的边去掉,再加入5条边有1种方法.
这样就可以用矩阵快速幂来求了。
dp[i][0] dp[i][1] dp[i][2]
0 0 0
0 0 0
*
1 4 5
1 5 6
0 0 1
=
dp[i+1][0] dp[i+1][1] dp[i+1][2]
0 0 0
0 0 0
总的复杂度为o( T * log(n) * 27);
这题竟然还卡常数,没办法,只好预处理一下矩阵。

#define N 3
#define M 100005
#define maxn 205
#define MOD 1000000007
struct node{
    ll c[N][N];
};
node one[100];
node mulnode(node a,node b){
    node c;
    FI(N){
        FJ(N){
            c.c[i][j] = 0;
            For(k,0,N){
                c.c[i][j] = (c.c[i][j] + a.c[i][k] * b.c[k][j]) % mod;
            }
        }
    }
    return c;
}
void Init(){
    memset(one[0].c,0,sizeof(one[0].c));
    one[0].c[0][0] = 1;
    one[0].c[0][1] = 4;
    one[0].c[0][2] = 5;
    one[0].c[1][0] = 1;
    one[0].c[1][1] = 5;
    one[0].c[1][2] = 6;
    one[0].c[2][0] = 0;
    one[0].c[2][1] = 0;
    one[0].c[2][2] = 1;
    For(i,1,100){
        one[i] = mulnode(one[i-1],one[i-1]);
    }
}
ll twoMulti(ll x){
    node ans;
    memset(ans.c,0,sizeof(ans.c));
    ans.c[0][0] = 1;
    ans.c[0][1] = 5;
    ans.c[0][2] = 6;
    int i = 0;
    while(x){
        if(x&1){
           ans = mulnode(ans,one[i]);
        }
        x/=2;
        i++;
    }
    return ans.c[0][2];
}
int T;
ll nn;
int main()
{
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    Init();
     while(S(T)!=EOF)
    {
        while(T--)
        {
            scanf("%lld",&nn);
            printf("%lld\n",twoMulti(nn - 1));
        }
    }
    //fclose(stdin);
    //fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值