小H为了参加NOIP复赛坐了一趟高铁,高铁的每节车厢里有n行m列的座位,这天恰好同时有n×m个人买了这节车厢的车票。每个人的车票可以看成一个二元组(x,y),对应于第x行y列的一个座位。
当然,有的人喜欢坐在过道边上,有的人则喜欢坐窗边,他们就会找附近的人换位置。不过,如果一名乘客离自己原来的座位太远检票员就会生气。检票员不会生气当且仅当每个人都坐在自己原来的位置上或相邻的位置上,即车票在(x,y)位置的人只可以坐在(x,y)、(x-1,y)、(x,y-1)、(x+1,y)和(x,y+1)这五个位置上。
幸运的是,最终每个位置上都坐着一名乘客,检票员也没有生气。问总共有多少种满足条件的座位排列方式。结果可能很大,请求出答案对998,244,353取模后的结果(注:998,244,353是一个质数)。
输入文件仅一行,包含两个正整数 n 和 m,表示行数和列数。
输出文件仅一行一个非负整数,即答案对 998,244,353 取模后的结果。

插头套矩阵快速幂。
#pragma GCC optimize("-Ofast")
#include<bits/stdc++.h>
#define mo 998244353
#define LL long long
using namespace std;
struct Mat{
LL a[267][267];
int l,r;
void mul(Mat X,Mat y){
memset(a,0,sizeof a);
l=X.l; r=y.r;
for (int i=1;i<=l;i++)
for (int j=1;j<=r;j++) {
for (int k=1;k<=X.r;k++)
a[i][j]+=X.a[i][k]*y.a[k][j]%mo;
a[i][j]%=mo;
}
}
void init(int x){
l=r=x;
memset(a,0,sizeof a);
for (int i=1;i<=x;i++) a[i][i]=1;
}
}A,B,C;
int n,m;
int p[267],to;
int ans,usd[1000009];
int dX[5]={0,1,-1,0,0},dY[5]={0,0,0,1,-1};
int will[7],Will[7],now[7],Now[7],tag;
void Dfs(int x){
if (x==m) {
int ans=0;
for (int i=m-1;~i;i--) ans=ans<<1|Will[i];
for (int i=m-1;~i;i--) ans=ans<<1|Now[i];
B.a[tag][lower_bound(p+1,p+to,ans)-p]++;
return;
}
if (!now[x]) {Dfs(x+1); return;}
if (x>0 && Now[x-1]==0) {
Now[x-1]=1; Dfs(x+1); Now[x-1]=0;
}
if (x<m-1 && Now[x+1]==0) {
Now[x+1]=1; Dfs(x+1); Now[x+1]=0;
}
if (!Now[x]) {
Now[x]=1; Dfs(x+1); Now[x]=0;
}
Will[x]=1; Dfs(x+1); Will[x]=0;
}
#define L(x) (x&-x)
int bitcount(int x){
if (!x) return 0;
return 1+bitcount(x-L(x));
}
void getsol() {
for (int i=0;i<(1<<m*2);i++)
if (bitcount(i)==m) p[++to]=i;
B.l=B.r=to;
for (int i=1;i<=to;i++) {
tag=i;
for (int l=0;l<m;l++) now[l]=(p[i]>>l)&1;
for (int l=m;l<2*m;l++) will[l-m]=(p[i]>>l)&1;
for (int l=0;l<m;l++) Now[l]=will[l],Will[l]=0;
Dfs(0);
}
}
signed main() {
scanf("%d%d",&n,&m);
getsol();
A.l=1; A.r=to; A.a[1][1]=1;
for (C.init(to);n;n>>=1,B.mul(B,B))
if (n&1) C.mul(C,B);
A.mul(A,C);
// for (int i=1;i<=10;i++) A.mul(A,B),
printf("%lld\n",A.a[1][1]);
}