P7943 题解
D1
考虑将贡献分为两种——字符串内部的贡献和拼接处产生的贡献。第一种贡献直接暴力求即可,重点是第二种。考虑一个有长度为
但是这会忽略一种特殊情况,试想有这样一组数据:
-1
2 3 2
aba
cde
正确答案显然为 aba 的前后缀有相同的字符,计算答案时会「自己和自己拼接」而造成贡献,我们可以记录多少个字符串有着极大前后缀字符均为
D2
考虑将贡献分五种情况:
- 单个串内部;
- 纯由相同字母组成的串拼接;
- 一段后缀 + 几个相同字母组成的串;
- 几个相同字母组成的串 + 一段前缀;
- 一段后缀 + 几个相同字母组成的串 + 一段前缀。
分别考虑这些情况,计算答案即可。公式的推导与 easy version 类似,这里不再重复。
设
int Gx(int i, int ed, int st) {
if (ed == 0 && st == 0) {
int res = m + k - (i - 1) * m - 1;
if (res > m) res = i * m - k + 1;
if (res < 0) res = 0;
return res;
}
if (ed == 0 || st == 0) {
int j = ed | st;
int res = j + k - 1 - (i - 1) * m - j;
if (res > m) res = i * m + j - k + 1;
if (res > j) res = j;
if (res < 0) res = 0;
return res;
}
int mn = min(st, ed), mx = max(st, ed);
int res = mn + k - 1 - i * m - mn;
if (res > mx) res = i * m + mn + mx - k + 1;
if (res > mn) res = mn;
if (res < 0) res = 0;
return res;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m >> k;
jc[0] = jcinv[0] = 1;
re (i, n)
jc[i] = 1ll * jc[i - 1] * i % mo, jcinv[i] = Inv(jc[i]);
re (i, n) {
string s;
cin >> s;
int f = s[0], b = s.back();
int l = s.find_first_not_of(f), r = m - s.find_last_not_of(b) - 1;
f -= 'a', b -= 'a';
if (l < 0)
l = n, ++all[f];
else {
++pre[f][l], ++suf[b][r];
if (f == b) ++same[f][l][r];
}
int len = 1;
rep (i, l, m - r - 1) {
if (s[i] == s[i - 1])
++len;
else
len = 1;
if (len >= k) ++ans;
}
}
rep (c, 0, 25) {
pre[c][0] = suf[c][0] = 1;
rep (mid, 0, all[c]) {
rep (ed, 0, m - 1) {
rep (st, 0, m - 1) {
int qw = Gx(mid, ed, st);
if (qw <= 0) continue;
int glx = (1ll * suf[c][ed] * pre[c][st] - same[c][st][ed] + mo) % mo * A(all[c], mid) %
mo * (n - mid + 1 - bool(st) - bool(ed)) % mo,
gly = A(n, mid + bool(st) + bool(ed));
ans += 1ll * qw * glx % mo * Inv(gly) % mo, umod(ans);
}
}
}
}
cout << 1ll * ans * jc[n] % mo << '\n';
}