传送门
思路
对于前 的数据,就是普通的项链问题,直接 解决。
对于 为偶数的情况,考虑一次填两个空,每次填 和 。
设 表示从 填到 时,前半部分的结尾(不0)等于(前1后2)半部分的开头,后半部分的结尾(不0)等于(前2后1)半部分的开头时的方案数。有:
大力构造状态转移矩阵即可。事实上,当考虑第二颗珠子及以后的珠子时,不存在都为 或者都为 的状态,所以只需要考虑 种状态,是不是很少呢?
特别需要注意的是边界条件。当 时,只有 ,其它都等于 。乘以 次转移矩阵就转移到了 的情况。最终答案不要加上与对面相等的情况(2)。
具体的转移矩阵见代码。代码假设 足够大,不会减出负数。
参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
using LL = long long;
using ULL = unsigned long long;
using std::cin;
using std::cout;
using std::endl;
using INT_PUT = int;
INT_PUT readIn()
{
INT_PUT a = 0;
bool positive = true;
char ch = getchar();
while (!(std::isdigit(ch) || ch == '-')) ch = getchar();
if (ch == '-')
{
positive = false;
ch = getchar();
}
while (std::isdigit(ch))
{
(a *= 10) -= ch - '0';
ch = getchar();
}
return positive ? -a : a;
}
void printOut(INT_PUT x)
{
char buffer[20];
int length = 0;
if (x < 0) putchar('-');
else x = -x;
do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
do putchar(buffer[--length]); while (length);
}
const int mod = 998244353;
int n, m;
template<int size>
struct Matrix
{
int c[size][size];
Matrix() : c() {};
explicit Matrix(bool) : Matrix()
{
for (int i = 0; i < size; i++)
c[i][i] = 1;
}
int* operator[](int x) { return c[x]; }
const int* operator[](int x) const { return c[x]; }
Matrix operator*(const Matrix& b) const
{
Matrix ret;
for (int i = 0; i < size; i++)
for (int k = 0; k < size; k++) if (c[i][k])
for (int j = 0; j < size; j++)
ret.c[i][j] = (ret.c[i][j] +
(LL)c[i][k] * b.c[k][j]) % mod;
return ret;
}
Matrix operator^(int y) const
{
Matrix ret(true);
Matrix x = *this;
while (y)
{
if (y & 1) ret = ret * x;
x = x * x;
y >>= 1;
}
return ret;
}
};
#define RunInstance(x) delete new x
struct cheat
{
using Matrix = Matrix<2>;
Matrix base;
cheat()
{
if (n == 1)
{
printOut(m % mod);
return;
}
base[0][0] = 0;
base[0][1] = m - 1;
base[1][0] = 1;
base[1][1] = m - 2;
Matrix ans = base ^ (n - 1);
printOut((LL)m * (m - 1) % mod * ans[1][0] % mod);
}
};
struct work
{
using Matrix = Matrix<7>;
work()
{
Matrix base;
/*
* 0: None
* 1: Self
* 2: Opposite
* 0 - 0 0
* 1 - 0 1
* 2 - 0 2
* 3 - 1 0
* 4 - 1 1
* 5 - 2 0
* 6 - 2 2
*/
base[0][0] = (std::max(LL(0), (LL)(m - 3) * (m - 3) - (m - 4))) % mod;
base[0][1] = m - 3;
base[0][2] = m - 3;
base[0][3] = m - 3;
base[0][4] = 1;
base[0][5] = m - 3;
base[0][6] = 1;
base[1][0] = (std::max(LL(0), (LL)(m - 2) * (m - 3) - (m - 3))) % mod;
base[1][1] = 0;
base[1][2] = m - 3;
base[1][3] = m - 2;
base[1][4] = 0;
base[1][5] = m - 2;
base[1][6] = 1;
base[2][0] = (std::max(LL(0), (LL)(m - 2) * (m - 3) - (m - 3))) % mod;
base[2][1] = m - 3;
base[2][2] = 0;
base[2][3] = m - 2;
base[2][4] = 1;
base[2][5] = m - 2;
base[2][6] = 0;
base[3][0] = (std::max(LL(0), (LL)(m - 2) * (m - 3) - (m - 3))) % mod;
base[3][1] = m - 2;
base[3][2] = m - 2;
base[3][3] = 0;
base[3][4] = 0;
base[3][5] = m - 3;
base[3][6] = 1;
base[4][0] = (std::max(LL(0), (LL)(m - 2) * (m - 2) - (m - 2))) % mod;
base[4][1] = 0;
base[4][2] = m - 2;
base[4][3] = 0;
base[4][4] = 0;
base[4][5] = m - 2;
base[4][6] = 1;
base[5][0] = (std::max(LL(0), (LL)(m - 2) * (m - 3) - (m - 3))) % mod;
base[5][1] = m - 2;
base[5][2] = m - 2;
base[5][3] = m - 3;
base[5][4] = 1;
base[5][5] = 0;
base[5][6] = 0;
base[6][0] = (std::max(LL(0), (LL)(m - 2) * (m - 2) - (m - 2))) % mod;
base[6][1] = m - 2;
base[6][2] = 0;
base[6][3] = m - 2;
base[6][4] = 1;
base[6][5] = 0;
base[6][6] = 0;
base = base ^ ((n >> 1) - 1);
LL ans = ((LL)base[4][0] + base[4][1] + base[4][3] + base[4][4]) % mod;
printOut(ans * m % mod * (m - 1) % mod);
}
};
void run()
{
n = readIn();
m = readIn();
if (n & 1)
RunInstance(cheat);
else
RunInstance(work);
}
int main()
{
#ifndef LOCAL
freopen("LiaPo.in", "r", stdin);
freopen("LiaPo.out", "w", stdout);
#endif
run();
return 0;
}
总结
增加状态,同时转移。