九校联考DAY1T3(dp,矩阵快速幂,十进制快速幂)

题目描述

金企鹅同学非常擅长用 1 X 2 的多米诺骨牌覆盖棋盘的题。有一天,正在背四六级单词的他忽然想:既然两个格子的积木叫“多米诺 (domino)”,那么三个格子的的积木一定叫“三米诺 (tromino)”了!用三米诺覆盖棋盘的题怎么做呢?

用三米诺覆盖 3 X n 的矩形棋盘,共多少种方案?三米诺可旋转;两种方案不同当且仅当这两种图案直接覆盖在一起无法重叠。

例如 n = 2 时,共 3 种方案:

输入格式

一行一个整数 n(n ≤ 1040000 10 40000 ),表示棋盘列数。

输出格式

一行一个整数,表示方案数,对 998244353 取模。

样例数据

input1

2

output1

3

input2

3

output2

10

input3

29

output3

543450786

数据规模与约定

对于 10% 的数据,n ≤ 5;

对于 30% 的数据,n ≤ 106 10 6

对于 40% 的数据,n ≤ 20001000;

对于 60% 的数据,n ≤ 109 10 9

对于 80% 的数据,n ≤ 101000 10 1000

对于 100% 的数据,n ≤ 1040000 10 40000

时间限制: 1s 1 s

空间限制: 512MB 512 MB


看着就像快速幂吧qaq
大力枚举一下转移的情况
可以写出方程
用个高斯消元或者直接用桶就可以了
消元后的方程
fi=fi1+2fi2+6fi3+fi4fi6 f i = f i − 1 + 2 ∗ f i − 2 + 6 ∗ f i − 3 + f i − 4 − f i − 6
矩阵快速幂优化
最后40分?
十进制快速幂了解一下

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
#define M martix
const int p = 998244353;
struct martix{ll f[6][6];};
martix A; 
M last;
char s[40010];
int n;
int read()
{
    int sum = 0;char c = getchar();bool flag = true;
    while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
    while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();
    if(flag) return sum;
    else return -sum;
}
void New(M &a)
{
    rep(i,0,5)
    rep(j,0,5) 
    if(i != j) a.f[i][j] = 0;
    else a.f[i][j] = 1;
}
M mul(M a,M b,bool flag)
{
    M ans;
    for(int i = 0;i <= (flag?5:0);++i)
    rep(j,0,5)
    {
        ans.f[i][j] = 0;
        rep(k,0,5) ans.f[i][j] = (1ll*ans.f[i][j] + 1ll*a.f[i][k]*b.f[k][j]%p)%p; 
    }
    return ans;
}
void pre()
{
    #define F(i,j,k) A.f[i][j] = k;
    F(0,0,1);F(1,0,2);F(2,0,6);F(3,0,1);F(5,0,p-1);
    F(0,1,1);F(1,2,1);F(2,3,1);F(3,4,1);F(4,5,1);
}
M Pow(M a,int k)
{
    M ans;New(ans);
    while(k)
    {
        if(k & 1) ans = mul(ans,a,1);
        a = mul(a,a,1);
        k >>= 1;
    }
    return ans;
}
M PPow(M a)
{
    M ans;New(ans);
    repp(i,n,1)
    {
        if(s[i] != '0')
        {
            M tmp = Pow(a,s[i]-'0');
            ans = mul(ans,tmp,1);
        }
        a = Pow(a,10);
    }
    return ans;
} 
void work()
{
    scanf("%s",s+1);
    n = strlen(s+1);
    if(n == 1 && s[1] == '1'){printf("1\n");exit(0);}
    else if(n == 1 && s[1] == '2') {printf("3\n");exit(0);}
    else if(n == 1 && s[1] == '3') {printf("10\n");exit(0);}
    s[n] -= 3;
    int i = n;while(s[i] < '0') s[i] += 10 , s[i-1] -= 1 , i--;
    A = PPow(A);
    last.f[0][0] = 10;last.f[0][1] = 3;last.f[0][2] = 1;
    last.f[0][3] = 1;last.f[0][4] = 0;last.f[0][5] = 0;
    last = mul(last,A,0);
    printf("%d",last.f[0][0]);
}
int main()
{
    freopen("tromino.in","r",stdin);
    freopen("tromino.out","w",stdout);
    pre();
    work();
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值