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;
}
posted @ 2024-12-06 23:28  CJzdc  阅读(75)  评论(0)    收藏  举报