在线段树上dp。我无论如何也是想不出来的。
我直接说思路吧。就是建一颗[1, n]的线段树,节点域为a[3][3],表示该节点表示的区间首尾分别为x, y时方案数为a[x][y]。叶子节点的a数组的值与于该点的值有关:叶子节点的值z为0时有a[i][j] = (i == j ? 1 : 0),否则只有a[z][z]=1其余为0。那么其它节点的维护可以用如下函数写出:
void update(seg &lch, seg &rch) {
for(int l = 0; l < 3; l++) {
for(int r = 0; r < 3; r++) {
a[l][r] = 0;
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
if(matrix[i][j])
a[l][r] = (1LL * lch.a[l][i] * rch.a[j][r] + a[l][r]) % MOD;
}
}
}
这个函数我写在了结构体内部,为了方便我从0开始编号,matrix就是输入的那个矩阵
其他就很简单了,直接上代码。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 77777;
const int MOD = 777777777;
int matrix[3][3];
struct seg {
int l, r;
int a[3][3];
void update(seg &lch, seg &rch) {
for(int l = 0; l < 3; l++) {
for(int r = 0; r < 3; r++) {
a[l][r] = 0;
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
if(matrix[i][j])
a[l][r] = (1LL * lch.a[l][i] * rch.a[j][r] + a[l][r]) % MOD;
}
}
}
} Tree[MAXN * 4 + 10];
void Build(int u, int l, int r) {
Tree[u].l = l; Tree[u].r = r;
if(l >= r) {
int (*a)[3] = Tree[u].a;
a[0][0] = a[1][1] = a[2][2] = 1;
return ;
}
int mid = (l + r) / 2;
Build(u * 2, l, mid);
Build(u * 2 + 1, mid + 1, r);
Tree[u].update(Tree[u * 2], Tree[u * 2 + 1]);
}
void Ins(int u, int x, int p) {
if(Tree[u].l == Tree[u].r) {
memset(Tree[u].a, 0, sizeof(Tree[u].a));
if(x != 0) Tree[u].a[x - 1][x - 1] = 1;
else {
int (*a)[3] = Tree[u].a;
a[0][0] = a[1][1] = a[2][2] = 1;
}
return ;
}
if(Tree[u * 2].r >= p) Ins(u * 2, x, p);
else Ins(u * 2 + 1, x, p);
Tree[u].update(Tree[u * 2], Tree[u * 2 + 1]);
}
int work(int p, int x) {
Ins(1, x, p);
int res = 0, (*a)[3] = Tree[1].a;
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
res = (res + a[i][j]) % MOD;
return res;
}
int main() {
int n, ops;
scanf("%d%d", &n, &ops);
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
scanf("%d", &matrix[i][j]);
Build(1, 1, n);
for(int i = 1; i <= ops; i++) {
int p, x;
scanf("%d%d", &p, &x);
printf("%d\n", work(p, x));
}
return 0;
}