考虑到这个操作实际上就是 \(5\) 进制的不进位加法,其实也就是 \(5\) 进制下的异或。
同时因为是 \(5\) 进制,对于 \(x\in [1, 4]\),\(x\times 0, \cdots, x\times 4\) 刚好可以表示出 \(0\sim 4\)。
于是可以考虑类似 \(2\) 进制的线性基弄个 \(5\) 进制的线性基。
即令 \(w_i\) 为 \(1\sim i - 1\) 位都 \(= 0\) 且第 \(i\) 位 \(\not = 0\) 的基底。
因为有上面的性质,如果遇到了已经有基底的情况,是肯定可以把这一位消成 \(0\) 的。
对于询问,先看能不能凑出这个数。
如果不能凑出,显然为 \(0\)。
否则首先对于基底的选取肯定是唯一的,但是剩下自由元是可以随便选取的,于是令自由元个数为 \(c\),答案即为 \(5^c\)。
时间复杂度 \(\mathcal{O}((n + q)m^2)\)。
#include<bits/stdc++.h>
constexpr int p = 1e9 + 7, mod = 5, T[5][5] = {
{0, 0, 0, 0, 0},
{0, 1, 2, 3, 4},
{0, 3, 1, 4, 2},
{0, 2, 4, 1, 3},
{0, 4, 3, 2, 1}
};
const int maxn = 5e2 + 10;
int n, m;
struct node_t {
int a[maxn];
inline node_t() {memset(a, 0, sizeof(a));}
inline int operator [](int x) const {return a[x];}
inline int &operator [] (int x) {return a[x];}
inline bool empty() {
for (int i = 1; i <= m; i++)
if (a[i]) return false;
return true;
}
inline node_t operator * (const int &v) const {
node_t b;
for (int i = 1; i <= m; i++) b[i] = (a[i] * v) % mod;
return b;
}
inline node_t operator - (const node_t &b) const {
node_t c;
for (int i = 1; i <= m; i++) c[i] = (a[i] - b[i] + mod) % mod;
return c;
}
} w[maxn];
char s[maxn];
inline node_t in() {
scanf("%s", s + 1);
node_t a;
for (int j = 1; j <= m; j++)
a[j] = s[j] - 'a';
return a;
}
int main() {
scanf("%d%d", &n, &m);
int cnt = 0;
while (n--) {
node_t a = in();
for (int i = 1; i <= m; i++) {
if (! a[i])
continue;
if (! w[i][i]) {
w[i] = a;
break;
}
a = a - w[i] * T[w[i][i]][a[i]];
}
cnt += a.empty();
}
int ans = 1;
for (int i = 1; i <= cnt; i++)
ans = 1ll * ans * 5 % p;
int q;
scanf("%d", &q);
while (q--) {
node_t a = in();
for (int i = 1; i <= m; i++) {
if (! a[i]) continue;
a = a - w[i] * T[w[i][i]][a[i]];
}
printf("%d\n", a.empty() ? ans : 0);
}
return 0;
}
标签:Vasya,进制,832E,凑出,Codeforces,int,基底,inline
From: https://www.cnblogs.com/rizynvu/p/18186749