看到题,感觉预处理下 \(O(nm)\) 可以很容易的做到。

大概处理下每一个串作为前缀,后缀所可以选择的数的数量。

然后直接枚举,然后转移就行了。

再看一眼数据范围, \(n \le 10 ^ 15\)

那没事了,很显然的矩乘的数据范围。

但是矩乘就不可以这么做了,要换一种转移方法。

你考虑把矩阵建成一个 \(50 \times 50\) 的矩阵,每一位 \((a,b)\) 的意义是 \(a\) 后面能否有 \(b\)

然后直接转移就行了。

感觉挺裸的,所以没太详细讲,不会矩乘的可以看下我的 博客

也可以私信。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define Rep(x, a, b) for(int x = a; x <= b; ++ x)
#define Dep(x, a, b) for(int x = a; x >= b; -- x)
#define Next(i, x) for(int i = head[x]; i; i = e[i].nxt)
int read() {
    char c = getchar(); int f = 1, x = 0;
    while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
    while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}
const int XRZ = 1e9 + 7;
char s[3]; int n, ans, p = 1, x, y;
struct node { int a[53][53], n, m; node() {memset(a, 0, sizeof(a)); }} a, b, c;
node operator * ( node &a, const node &b) {
	node c; c.n = a.n; c.m = b.m;
	Rep(i, 0, a.n - 1) Rep(j, 0, a.m - 1) Rep(k, 0, b.m - 1) c.a[i][k] = (c.a[i][k] + a.a[i][j] * b.a[j][k]) % XRZ;
	return c;
}
node qpow(int y) {
    node ans = c, base = b;
    while(y) {
        ans = ans * ans;
        if(y & n) ans = ans * base;
        y >>= 1;
    } return ans;
}
signed main() {
	n = read() - 1; int m = read(), k = read();
	a.n = 1, a.m = m, b.n = b.m = m, c.n = c.m = m;
    Rep(i, 0, m - 1) {
		a.a[0][i] = 1, c.a[i][i] = 1;
        Rep(j, 0, m - 1) b.a[i][j] = 1;
	}
    Rep(i, 0, k - 1) { scanf("%s", s);
		if(s[0] >= 'a' && s[0] <= 'z') x = s[0] - 'a';
		else x = s[0] - 'A' + 26;
		if(s[1] >= 'a' && s[1] <= 'z') y = s[ 1 ] - 'a';
		else y = s[1] - 'A' + 26; b.a[y][x] = 0;
	}
	while(p <= n) p <<= 1; p >>= 1;
    a = a * qpow(p);
    Rep(i, 0, m - 1) ans = (ans + a.a[0][i]) % XRZ;
	printf("%lld\n", ans);
	return 0;
}