#include #define unlikely(x) __builtin_expect(!!(x), 0) struct char_reader { FILE *f; char *buf, *p1, *p2; int size; char_reader(FILE *fin, int bufsize = 1 << 16) { f = fin; size = bufsize; p1 = p2 = 0; buf = new char[size]; } ~char_reader() { delete[] buf; } inline int operator()() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, size, f), p1 == p2) ? EOF : *p1++; } }; struct char_writer { FILE *f; char *buf, *p, *end; int size; char_writer(FILE *fout, int bufsize = 1 << 16) { f = fout; size = bufsize; buf = new char[size]; p = buf; end = buf + bufsize; } ~char_writer() { fwrite(buf, p - buf, 1, f); delete[] buf; } inline char operator()(char ch) { if (unlikely(end == p)) { fwrite(buf, end - buf, 1, f); p = buf; } return *p++ = ch; } }; char_reader gch(stdin); char_writer wch(stdout); template inline int read(T &t) { bool f = false; int ch; while (ch = gch(), !((ch >= '0' && ch <= '9') || ch == '-')) { if (ch == EOF) return 0; } t = 0; if (ch == '-') f = true, ch = gch(); t = ch ^ 48; while (ch = gch(), ch >= '0' && ch <= '9') t = (t << 3) + (t << 1) + (ch ^ 48); if (f) t = -t; return 1; } inline int read(char &c) { c = 0; int ch; while (ch = gch(), (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t')) { if (ch == EOF) return 0; } c = ch; return 1; } inline int read(char *s) { int ch; while (ch = gch(), (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t')) { if (ch == EOF) return 0; } *s++ = ch; while (ch = gch(), !(ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') && ch != EOF) *s++ = ch; *s++ = 0; return 1; } inline int read(const char *s) { return read((char *)s); } inline int readline(char *s) { int ch; while (ch = gch(), (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t')) { if (ch == EOF) return 0; } *s++ = ch; while (ch = gch(), !(ch == '\n' || ch == '\r') && ch != EOF) *s++ = ch; *s++ = 0; return 1; } inline int readline(const char *s) { return readline((char *)s); } template inline void write(T t) { int stk[20], cnt = 0; if (t == 0) { wch('0'); return; } if (t < 0) { wch('-'); t = -t; } while (t > 0) { stk[cnt++] = t % 10; t /= 10; } while (cnt) wch(stk[--cnt] + '0'); } inline void write(char t) { wch(t); } inline void write(char *s) { while (*s) wch(*s++); } inline void write(const char *s) { write((char *)s); } #if __cplusplus >= 201103L template inline int read(T &t, Args &...args) { return read(t) + read(args...); } template inline void write(T t, Args... args) { write(t); write(args...); } #else template inline int read(A_t &a, B_t &b) { return read(a) + read(b); } template inline int read(A_t &a, B_t &b, C_t &c) { return read(a) + read(b) + read(c); } template inline int read(A_t &a, B_t &b, C_t &c, D_t &d) { return read(a) + read(b) + read(c) + read(d); } template inline void write(A_t a, B_t b) { write(a); write(b); } template inline void write(A_t a, B_t b, C_t c) { write(a); write(b); write(c); } template inline void write(A_t a, B_t b, C_t c, D_t d) { write(a); write(b); write(c); write(d); } #endif #include #include #include using namespace std; typedef long long LL; const LL mod = 998244353; const int maxc = 1e6 + 10; LL ans_all = 0; int n, m; char **mp; struct Node { LL cur, sub_tree_sum; set pos; Node *nxt[10]; Node() { cur = 0; sub_tree_sum = 0; for (int i = 0; i < 10; i++) nxt[i] = nullptr; } } *const root = new Node; void InitDfs(Node *p) { LL delta = ((LL)n * (n + 1) / 2) % mod; int last_pos = 0; for (auto it = p->pos.begin(); it != p->pos.end(); ++it) { LL gap = *it - last_pos - 1; delta -= (gap * (gap + 1) / 2) % mod; delta %= mod; last_pos = *it; } LL gap = n - last_pos; delta -= (gap * (gap + 1) / 2) % mod; delta = (delta + mod) % mod; if (p != root) { ans_all += delta; ans_all %= mod; p->cur = delta; p->sub_tree_sum = delta; } for (int i = 0; i < 10; i++) if (p->nxt[i] != nullptr) { InitDfs(p->nxt[i]); p->sub_tree_sum += p->nxt[i]->sub_tree_sum; p->sub_tree_sum %= mod; } } void Build() { for (int i = 1; i <= n; i++) { Node *p = root; for (int j = 1; j <= m; j++) { if (p->nxt[mp[i][j] - '0'] == nullptr) p->nxt[mp[i][j] - '0'] = new Node; p = p->nxt[mp[i][j] - '0']; p->pos.insert(i); } } InitDfs(root); } Node *merge_root; void Merge(Node *dst, Node *src) { LL delta = dst->cur; LL change = 0; for (auto it = src->pos.begin(); it != src->pos.end(); ++it) { int new_pos = *it; auto new_pos_it = dst->pos.lower_bound(new_pos); if (*new_pos_it == new_pos) throw "Two different string in the same position!"; int left_pos = 0, right_pos = n + 1; if (new_pos_it != dst->pos.begin()) { new_pos_it--; left_pos = *new_pos_it; new_pos_it++; } if (new_pos_it != dst->pos.end()) { right_pos = *new_pos_it; } LL old_gap = right_pos - left_pos - 1; LL new_gap1 = new_pos - left_pos - 1, new_gap2 = right_pos - new_pos - 1; delta = ((delta + old_gap * (old_gap + 1) / 2 - new_gap1 * (new_gap1 + 1) / 2 - new_gap2 * (new_gap2 + 1) / 2) % mod + mod) % mod; dst->pos.insert(new_pos); } dst->sub_tree_sum -= dst->cur; // change = delta - dst->cur; dst->cur = delta; dst->sub_tree_sum += dst->cur; dst->sub_tree_sum %= mod; // if (dst != merge_root) { // ans_all += change; // ans_all %= mod; // } bool has_covered[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; for (int i = 0; i < 10; i++) if (src->nxt[i] != nullptr) { has_covered[i] = true; if (dst->nxt[i] == nullptr) { dst->nxt[i] = new Node; } if (src->nxt[i]->pos.size() > dst->nxt[i]->pos.size()) { dst->sub_tree_sum -= dst->nxt[i]->sub_tree_sum; src->sub_tree_sum -= src->nxt[i]->sub_tree_sum; swap(dst->nxt[i], src->nxt[i]); dst->sub_tree_sum += dst->nxt[i]->sub_tree_sum; src->sub_tree_sum += src->nxt[i]->sub_tree_sum; dst->sub_tree_sum %= mod; src->sub_tree_sum %= mod; } dst->sub_tree_sum -= dst->nxt[i]->sub_tree_sum; Merge(dst->nxt[i], src->nxt[i]); dst->sub_tree_sum += dst->nxt[i]->sub_tree_sum; dst->sub_tree_sum %= mod; } for (int i = 0; i < 10; i++) if (!has_covered[i] && dst->nxt[i] != nullptr) { // ans_all += dst->nxt[i]->sub_tree_sum; // ans_all %= mod; } delete src; } void Merging() { Node *p = root; for (int progress = 1; progress <= m - 1; progress++) { int max_sub_size = 0, nid = -1; for (int i = 0; i < 10; i++) if (p->nxt[i] != nullptr) { if (p->nxt[i]->pos.size() > max_sub_size) { max_sub_size = p->nxt[i]->pos.size(); nid = i; } } if (max_sub_size == 0) throw "No Sub Tree!"; bool have_things_merged = false; for (int i = 0; i < 10; i++) if (p->nxt[i] != nullptr && i != nid) { merge_root = p->nxt[nid]; Merge(p->nxt[nid], p->nxt[i]); have_things_merged = true; } // if (!have_things_merged) { // ans_all = ((ans_all - p->nxt[nid]->sub_tree_sum) % mod + mod) % mod; for (int i = 0; i < 10; i++) if (p->nxt[nid]->nxt[i] != nullptr) { ans_all += p->nxt[nid]->nxt[i]->sub_tree_sum; ans_all %= mod; } // } // ans_all = (ans_all + p->nxt[nid]->sub_tree_sum) % mod; p = p->nxt[nid]; } } int main() { #ifdef local freopen("pro.in", "r", stdin); #endif read(n, m); mp = new char *[n + 2]; for (int i = 1; i <= n; i++) mp[i] = new char[m + 3]; for (int i = 1; i <= n; i++) { read(mp[i] + 1); for (int j = 1; j <= m; j++) assert('0' <= mp[i][j] && mp[i][j] <= '9'); } Build(); Merging(); ans_all = (ans_all + mod) % mod; write(ans_all, '\n'); return 0; } /* 3 2 55 08 78 */