题目传送门:http://uoj.ac/problem/241
官方题解:http://c-sunshine.blog.uoj.ac/blog/2026
考试的时候,推这个题推了两个半小时QAQ
一直觉得马上就可以推出来辣!结果一直没有推出来
不过最后还是成功把奇数的式子推出来了(虽然和std不同,但是正确的)
至于偶数的式子嘛,感觉题解说的不是很清楚,我也就来哔哔两句:
不难发现,如果我们仍然按位来递推,不方便处理穿过发射台的情况
于是我们定义二元组(f,g)表示第i位与i+n/2位的颜色情况
于是我们发现这样的定义就可以按二元组为阶段来递推了,因为这货就无后效性了
于是搞一搞矩阵乘法即可
ps:为什么我做这题的时候老想着数位DP

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
#define S(x,y) ((x)*3+(y)+1)
using namespace std;
const int MOD = 998244353;
const int N = 100000;
const int M = 9;
int n,m,vout;
struct Matrix{
int a[M][M];
inline Matrix() {}
inline Matrix(int v) {memset(a,0,sizeof(a));for(int i=1;i<M;i++) a[i][i]=v;}
inline Matrix operator = (const Matrix &B) {memcpy(&(*this),&B,sizeof(a));}
inline Matrix operator * (const Matrix &B) {
Matrix ret(0);
for (int i=1;i<M;i++) for (int j=1;j<M;j++) for (int k=1;k<M;k++)
ret.a[i][j] = ((LL)a[k][j]*B.a[i][k] + ret.a[i][j]) % MOD;
return ret;
}
inline Matrix operator ^ (int t) {
Matrix ret(1),w=*this;
while (t) {
if (t & 1) ret = ret*w;
w = w*w; t >>= 1;
}
return ret;
}
};
inline int read(){
char c=getchar(); int ret=0,f=1;
while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
return ret*f;
}
inline int pow(int w, int t){
int ret = 1;
while (t) {
if (t & 1) ret = (LL)ret*w % MOD;
w = (LL)w*w % MOD; t >>= 1;
}
return ret;
}
int main(){
n = read(), m = read();
if (n%2 == 0) {
Matrix ori(0),tra(0); ori.a[S(1,2)][1]=1;
if (m > 3) tra.a[S(0,0)][S(1,2)] = tra.a[S(0,0)][S(2,1)] = (LL)(m - 2) * (m - 3) % MOD;
if (m > 3) tra.a[S(0,0)][S(1,0)] = tra.a[S(0,0)][S(0,1)] = (LL)(m - 3) * (m - 3) % MOD;
if (m > 3) tra.a[S(0,0)][S(2,0)] = tra.a[S(0,0)][S(0,2)] = (LL)(m - 3) * (m - 3) % MOD;
if (m > 3) tra.a[S(0,0)][S(0,0)] = ((LL)(m-4) * (m-4) + m - 3) % MOD;
if (m > 2) tra.a[S(1,0)][S(2,1)] = tra.a[S(0,1)][S(1,2)] = m - 2;
if (m > 2) tra.a[S(1,0)][S(0,1)] = tra.a[S(0,1)][S(1,0)] = m - 2;
if (m > 2) tra.a[S(1,0)][S(0,2)] = tra.a[S(0,1)][S(2,0)] = m - 2;
if (m > 3) tra.a[S(1,0)][S(2,0)] = tra.a[S(0,1)][S(0,2)] = m - 3;
if (m > 3) tra.a[S(1,0)][S(0,0)] = tra.a[S(0,1)][S(0,0)] = m - 3;
if (m > 2) tra.a[S(2,0)][S(1,2)] = tra.a[S(0,2)][S(2,1)] = m - 2;
if (m > 2) tra.a[S(2,0)][S(0,2)] = tra.a[S(0,2)][S(2,0)] = m - 2;
if (m > 2) tra.a[S(2,0)][S(0,1)] = tra.a[S(0,2)][S(1,0)] = m - 2;
if (m > 3) tra.a[S(2,0)][S(1,0)] = tra.a[S(0,2)][S(0,1)] = m - 3;
if (m > 3) tra.a[S(2,0)][S(0,0)] = tra.a[S(0,2)][S(0,0)] = m - 3;
tra.a[S(1,2)][S(0,1)] = tra.a[S(2,1)][S(1,0)] = 1;
tra.a[S(1,2)][S(2,0)] = tra.a[S(2,1)][S(0,2)] = 1;
tra.a[S(1,2)][S(2,1)] = tra.a[S(2,1)][S(1,2)] = 1;
tra.a[S(1,2)][S(0,0)] = tra.a[S(2,1)][S(0,0)] = 1;
tra = tra^(n/2-1); ori = ori * tra;
vout = ((LL)ori.a[S(0,2)][1] + ori.a[S(1,0)][1] + ori.a[S(1,2)][1] + ori.a[S(0,0)][1]) % MOD;
vout = ((LL)vout * m % MOD) * (m-1) % MOD;
printf("%d\n",vout);
}
else {
if (n == 3) printf("%d\n",((LL)(pow(m-1,n-1)-(m-1))*m % MOD + MOD) % MOD);
else {
Matrix ori(0); ori.a[1][1] = 1; ori.a[2][1] = m-2;
Matrix tra(0); tra.a[1][2] = 1; tra.a[2][1] = m-1; tra.a[2][2] = m-2;
tra = tra^(n-5); ori = ori * tra; ori.a[1][1] = (LL)ori.a[1][1]*(m-1)%MOD;
vout = (((LL)(m-1)*(m-2)%MOD)*ori.a[2][1]%MOD + (LL)ori.a[1][1]*(m-1)%MOD) % MOD;
vout = (LL)(pow(m-1,n-1)-vout)*m % MOD;
printf("%d\n",(vout+MOD)%MOD);
}
}
return 0;
}
—– UPD 2017.1.12 —–
今天听毛爷爷讲这个题
居然感觉自己只会做奇数情况 QwQ
其实重点是,这货偶数情况的转移似乎非常神奇的样子
定义状态的方式,非常值得借鉴