P11270 【MX-S5-T4】魔法少女们
不妨分类讨论 \(S_i\) 和 \(T_j\) 是否有交。
有交的情况是好做的,不妨令重叠部分长为 \(len\),此时 \(S_i\) 和 \(T_j\) 需要满足如下条件:
-
\(S_i\) 的长为 \(len\) 后缀与 \(T_j\) 的长为 \(len\) 前缀相同。
-
\(S_i\) 中未匹配的左括号数 与 \(T_j\) 的长为 \(|T_j|-len\) 的后缀中未匹配右括号数 相同。
不妨令 \(\sum|S_i|+\sum|T_j|=L\),这一部分可通过哈希以 \(O(L)\) 的复杂度解决。
考虑无交的情况。
不妨设 \(S_i\) 的长度为 \(len_1\),剩余左括号数为 \(c_1\);\(T_j\) 的长度为 \(len_2\),剩余右括号数为 \(c_2\)。
那么我们要求的就是从 \((\dfrac{len_1+c_1}{2},\dfrac{len_1-c_1}{2})\) 走到 \((\dfrac{k-len_2+c_2}{2},\dfrac{k-len_2-c_2}{2})\),且不越过直线 \(x=y\) 的方案数。
这是一个经典的反射容斥,把相同的 \((len,c)\) 合并一起做可以做到 \(O(L^{\frac{4}{3}})\)。
考虑怎么优化。注意到大多数 \((len,c)\) 状态的 \(len\) 都较小,考虑对 \(len+c\le B\) 的点暴力 dp,推到 \(x+y=B\) 的直线上。
然后较小的 \(len\) 就只有 \(O(B)\) 个状态了,\(len\) 较大的部分显然也只有 \(O(\dfrac{L}{B})\) 个状态,总状态数就是 \(O(\sqrt L)\)。这时再暴力枚举就是 \(O(L)\),可以通过。
代码写的很难看。
#include <bits/stdc++.h>
#define ALL(x) begin(x), end(x)
using namespace std;
void file() {
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
}
using ll = long long;
const int kMod = 1e9 + 7;
void Add(int& x, int y) { ((x += y) >= kMod) && (x -= kMod); }
void Sub(int& x, int y) { ((x -= y) < 0) && (x += kMod); }
int Sum(int x, int y) { return Add(x, y), x; }
int Dif(int x, int y) { return Sub(x, y), x; }
int qpow(int x, int y) {
int b = x, r = 1;
for(; y; b = (ll)b * b % kMod, y /= 2) {
if(y & 1) {
r = (ll)r * b % kMod;
}
}
return r;
}
const int kN = 2e5 + 5, kL = 1e6 + 5;
int sub, n, m, k;
array<string, kN> s, t;
array<int, kN> cnts, cntt, lens, lent;
namespace A {
namespace Hash {
using i128 = __int128_t;
const ll kMod = 1'000'000'000'000'030'07ll;
const ll base = 19260817;
array<ll, kL> pw;
array<vector<ll>, kN> sufs, pret;
void init() {
pw[0] = 1;
for(int i = 1; i < kL; i++) {
pw[i] = (i128)pw[i - 1] * base % kMod;
}
for(int i = 1; i <= n; i++) {
if(cnts[i] == -1) continue;
sufs[i].resize(lens[i], 0);
sufs[i].back() = s[i].back();
for(int j = lens[i] - 2; j >= 0; j--) {
sufs[i][j] = (sufs[i][j + 1] + (i128)pw[lens[i] - j - 1] * s[i][j]) % kMod;
}
}
for(int i = 1; i <= m; i++) {
if(cntt[i] == -1) continue;
pret[i].resize(lent[i], 0);
pret[i][0] = t[i][0];
for(int j = 1; j < lent[i]; j++) {
pret[i][j] = ((i128)base * pret[i][j - 1] + t[i][j]) % kMod;
}
}
}
}
array<vector<int>, kL> bucls, buclt, suft, buccs, bucct;
void init() {
for(int i = 1; i <= m; i++) {
if(cntt[i] == -1) continue;
suft[i].resize(lent[i] + 1, 0);
int x = 0;
for(int j = lent[i] - 1; j >= 0; j--) {
(t[i][j] == ')') ? x++ : x--;
suft[i][j] = x;
}
}
for(int i = 1; i <= n; i++) {
if(cnts[i] != -1) {
bucls[lens[i]].push_back(i);
}
}
for(int i = 1; i <= m; i++) {
if(cntt[i] != -1) {
buclt[lent[i]].push_back(i);
}
}
}
int solve() {
ll ans = 0;
Hash::init(), init();
vector<int> vecs, vect;
for(int i = 1; i <= k; i++) {
if(bucls[i].size() != 0) {
vecs.push_back(i);
}
if(buclt[i].size() != 0) {
vect.push_back(i);
}
}
for(int ls : vecs) {
for(int lt : vect) {
if(ls + lt > k) {
int len = ls + lt - k;
for(int i : bucls[ls]) buccs[cnts[i]].push_back(i);
for(int i : buclt[lt]) {
bucct[suft[i][len]].push_back(i);
}
for(int i : bucls[ls]) {
int c = cnts[i];
if(buccs[c].empty() || bucct[c].empty()) continue;
unordered_map <ll, int> ump;
for(int x : buccs[c]) {
ump[Hash::sufs[x][ls - len]]++;
}
for(int x : bucct[c]) {
ll hsh = Hash::pret[x][len - 1];
if(ump.count(hsh)) ans += ump[hsh];
}
buccs[c].clear(), bucct[c].clear();
}
for(int i : bucls[ls]) buccs[cnts[i]].clear();
for(int i : buclt[lt]) {
bucct[suft[i][len]].clear();
}
}
}
}
return ans % ::kMod;
}
}
namespace B {
namespace Math {
array<int, kL> mul, imul;
void init() {
mul[0] = 1;
for(int i = 1; i < kL; i++) {
mul[i] = (ll)mul[i - 1] * i % kMod;
}
imul[kL - 1] = qpow(mul[kL - 1], kMod - 2);
for(int i = kL - 2; i >= 0; i--) {
imul[i] = (ll)imul[i + 1] * (i + 1) % kMod;
}
}
int C(int n, int m) {
if(min({n, m, n - m}) < 0) {
return 0;
}
return (ll)mul[n] * imul[m] % kMod * imul[n - m] % kMod;
}
int f(int x1, int y1, int x2, int y2) {
int ans = 0;
Add(ans, C(x2 + y2 - x1 - y1, x2 - x1));
Sub(ans, C(x2 + y2 - x1 - y1, y2 - x1 - 1));
return ans;
}
}
const int kB = 2000;
int cs, ct, os, ot;
struct Node {
int x, y, cnt;
Node() { x = y = -1, cnt = 0; }
Node(int _x, int _y, int _c) {
x = _x, y = _y, cnt = _c;
}
};
bool operator < (Node x, Node y) {
return (x.x == y.x) ? (x.y < y.y) : (x.x < y.x);
}
bool operator == (Node x, Node y) {
return (x.x == y.x) && (x.y == y.y);
}
array<Node, kN * 2> ps, pt, ns, nt;
array<array<int, kB + 5>, kB + 5> f;
void dp(int lim) {
for(int i = 0; i <= lim; i++) {
for(int j = 0; j <= min(i, lim - i); j++) {
Add(f[i + 1][j], f[i][j]);
Add(f[i][j + 1], f[i][j]);
}
}
}
int calcs(int lim) {
for(auto& k : f) k.fill(0);
for(int i = 1; i <= n; i++) {
if(cnts[i] == -1) continue;
if(ps[i].x + ps[i].y <= lim) {
f[ps[i].x][ps[i].y]++;
ps[i] = Node();
}
}
dp(lim);
int ans = 0;
for(int i = 1; i <= m; i++) {
if(cntt[i] == -1) continue;
int x = k / 2 - pt[i].y;
int y = k / 2 - pt[i].x;
if((x >= 0) && (y >= 0) && (x + y <= lim)) {
Add(ans, f[x][y]);
}
}
for(int i = 0, j = lim; i <= lim; i++, j--) {
if(f[i][j] != 0) {
ps[++cs] = Node(i, j, f[i][j]);
f[i][j] = 0;
}
}
return ans;
}
int calct(int lim) {
for(auto& k : f) k.fill(0);
for(int i = 1; i <= m; i++) {
if(cntt[i] == -1) continue;
if(pt[i].x + pt[i].y <= lim) {
f[pt[i].x][pt[i].y]++;
pt[i] = Node();
}
}
dp(lim);
int ans = 0;
for(int i = 1; i <= n; i++) {
if(cnts[i] == -1) continue;
int x = k / 2 - ps[i].y;
int y = k / 2 - ps[i].x;
if((x >= 0) && (y >= 0) && (x + y <= lim)) {
Add(ans, f[x][y]);
}
}
for(int i = 0, j = lim; i <= lim; i++, j--) {
if(f[i][j] != 0) {
pt[++ct] = Node(i, j, f[i][j]);
f[i][j] = 0;
}
}
return ans;
}
int solve() {
Math::init();
for(int i = 1; i <= n; i++) {
if(cnts[i] != -1) {
ps[i] = Node((lens[i] + cnts[i]) / 2, (lens[i] - cnts[i]) / 2, 1);
}
}
for(int i = 1; i <= m; i++) {
if(cntt[i] != -1) {
pt[i] = Node((lent[i] + cntt[i]) / 2, (lent[i] - cntt[i]) / 2, 1);
}
}
int ans = 0, lim = max(min(kB, k / 4 - 1), 0);
cs = n, ct = m;
Add(ans, calcs(lim));
Add(ans, calct(lim));
sort(ps.data() + 1, ps.data() + cs + 1);
sort(pt.data() + 1, pt.data() + ct + 1);
for(int l = 1, r; l <= cs; l = r + 1) {
for(r = l; (r < cs) && (ps[r + 1] == ps[l]); r++) ;
int sum = 0;
for(int i = l; i <= r; i++) Add(sum, ps[i].cnt);
if(sum != 0) {
ns[++os] = Node(ps[l].x, ps[l].y, sum);
}
}
for(int l = 1, r; l <= ct; l = r + 1) {
for(r = l; (r < ct) && (pt[r + 1] == pt[l]); r++) ;
int sum = 0;
for(int i = l; i <= r; i++) Add(sum, pt[i].cnt);
if(sum != 0) {
nt[++ot] = Node(k / 2 - pt[l].y, k / 2 - pt[l].x, sum);
}
}
for(int i = 1; i <= os; i++) {
for(int j = 1; j <= ot; j++) {
if((ns[i].x <= nt[j].x) && (ns[i].y <= nt[j].y)) {
Add(ans, (ll)ns[i].cnt * nt[j].cnt % kMod * Math::f(ns[i].x, ns[i].y, nt[j].x, nt[j].y) % kMod);
}
}
}
return ans;
}
}
void init() {
for(int i = 1; i <= n; i++) {
int x = 0;
for(char c : s[i]) {
if(c == '(') x++;
else if(--x < 0) {
cnts[i] = -1, s[i].clear();
break;
}
}
if(cnts[i] != -1) {
cnts[i] = x;
lens[i] = s[i].size();
}
}
for(int i = 1; i <= m; i++) {
reverse(ALL(t[i]));
int x = 0;
for(char c : t[i]) {
if(c == ')') x++;
else if(--x < 0) {
cntt[i] = -1, t[i].clear();
break;
}
}
if(cntt[i] != -1) {
cntt[i] = x;
lent[i] = t[i].size();
}
reverse(ALL(t[i]));
}
}
int main() {
// file();
ios::sync_with_stdio(0); cin.tie(0);
cin >> sub >> n >> m >> k;
for(int i = 1; i <= n; i++) cin >> s[i];
for(int i = 1; i <= m; i++) cin >> t[i];
init();
int ans = 0;
Add(ans, A::solve());
Add(ans, B::solve());
cout << ans << "\n";
return 0;
}
浙公网安备 33010602011771号