2019 ICPC 南京 F. Paper Grading(字典树dfs序上树套树)

Paper Grading

题意:给定 n n n个字符串,有两种操作:

一、给定 i , j i, j i,j,交换第 i i i个跟第 j j j个字符串。

二、给定 str , k , l , r k, l, r k,l,r,问你在区间 [ l , r ] [l, r] [l,r]中的字符,与 str 至少有 k k k个公共前缀的字符串有多少个。

先建立一颗字典树,对于满足要求的字符串,我们可以在字典树中查找 str 的第 k k k 个字符落在的点 r t rt rt

然后在以 r t rt rt 为根的子树上查找在区间 [ l , r ] [l, r] [l,r]的字符有多少个,

考虑在字典树 d f s dfs dfs序上,对每个节点建立一颗主席树,然后插入值,对于查询操作,我们只要查找两颗主席树上 [ l , r ] [l, r] [l,r]区间的和即可。

但是因为有带修操作,所以考虑树状数组套主席树,这样即可满足带修跟查找操作,发现一直 w a   7 wa\ 7 wa 7,(据我判断应该是内存不够)。

所以考虑如何优化,对于字典树来说,总长度可以说是固定的 2 × 1 0 5 2 \times 10 ^ 5 2×105,但是对于 n n n来说却是一般小于 2 × 1 0 5 2 \times 10 ^5 2×105的,对于上述的建树方式,

我们建立的主席树可以说也是固定的 2 × 1 0 5 2 \times 10 ^ 5 2×105棵,所以说整体来说是 2 × 1 0 5 log ⁡ n log ⁡ n 2 \times 10 ^ 5 \log n \log n 2×105lognlogn

如果对每个字符串建立一颗主席树整体来说应该是 n log ⁡ 2 × 1 0 5 log ⁡ 2 × 1 0 5 n \log {2 \times 10 ^ 5} \log {2 \times 10 ^ 5} nlog2×105log2×105,在多数情况下应该是小于上着的。

所以对于查询我们只要对 [ l , r ] [l, r] [l,r]区间,查询 d f s dfs dfs序在 l [ r t ] , r [ r t ] l[rt], r[rt] l[rt],r[rt]之间的即可,对于修改操作我们交换 i , j i, j i,j位置的 d f s dfs dfs序即可。

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 10;

int trie[N][26], cnt = 1;

int pos[N], value[N], l[N], r[N], tot, n, m;

int root[N], ls[N << 7], rs[N << 7], sum[N << 7], num;

int insert(string &str) {
  int n = str.size(), rt = 1;
  for (int i = 0; i < n; i++) {
    if (!trie[rt][str[i] - 'a']) {
      trie[rt][str[i] - 'a'] = ++cnt;
    }
    rt = trie[rt][str[i] - 'a'];
  }
  return rt;
}

void dfs(int rt) {
  l[rt] = ++tot;
  for (int i = 0; i < 26; i++) {
    if (trie[rt][i]) {
      dfs(trie[rt][i]);
    }
  }
  r[rt] = tot;
}

void update(int &rt, int l, int r, int x, int v) {
  if (!rt) {
    rt = ++num;
  }
  sum[rt] += v;
  if (l == r) {
    return ;
  }
  int mid = l + r >> 1;
  if (x <= mid) {
    update(ls[rt], l, mid, x, v);
  }
  else {
    update(rs[rt], mid + 1, r, x, v);
  }
}

int A[50], B[50], cnt1, cnt2;

int query_sum(int l, int r, int L, int R) {
  if (l >= L && r <= R) {
    int ans = 0;
    for (int i = 1; i <= cnt1; i++) {
      ans -= sum[A[i]];
    }
    for (int i = 1; i <= cnt2; i++) {
      ans += sum[B[i]];
    }
    return ans;
  }
  int mid = l + r >> 1, ans = 0, A1[50], B1[50];
  if (L <= mid) {
    for (int i = 1; i <= cnt1; i++) {
      A1[i] = A[i];
      A[i] = ls[A[i]];
    }
    for (int i = 1; i <= cnt2; i++) {
      B1[i] = B[i];
      B[i] = ls[B[i]];
    }
    ans += query_sum(l, mid, L, R);
    for (int i = 1; i <= cnt1; i++) {
      A[i] = A1[i];
    }
    for (int i = 1; i <= cnt2; i++) {
      B[i] = B1[i];
    }
  }
  if (R > mid) {
    for (int i = 1; i <= cnt1; i++) {
      A1[i] = A[i];
      A[i] = rs[A[i]];
    }
    for (int i = 1; i <= cnt2; i++) {
      B1[i] = B[i];
      B[i] = rs[B[i]];
    }
    ans += query_sum(mid + 1, r, L, R);
    for (int i = 1; i <= cnt1; i++) {
      A[i] = A1[i];
    }
    for (int i = 1; i <= cnt2; i++) {
      B[i] = B1[i];
    }
  }
  return ans;
}

inline int lowbit(int x) {
  return x & (-x);
}

void add(int pos, int x, int v) {
  for (int i = pos; i <= n; i += lowbit(i)) {
    update(root[i], 1, tot, x, v);
  }
}

int get_sum(int l, int r, int L, int R) {
  if (l > r || L > R) {
    return 0;
  }
  cnt1 = cnt2 = 0;
  for (int i = l - 1; i; i -= lowbit(i)) {
    A[++cnt1] = root[i];
  }
  for (int i = r; i; i -= lowbit(i)) {
    B[++cnt2] = root[i];
  }
  return query_sum(1, tot, L, R);
}

int main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> m;
  string str;
  for (int i = 1; i <= n; i++) {
    cin >> str;
    pos[i] = insert(str);
  }
  dfs(1);
  for (int i = 1; i <= n; i++) {
    pos[i] = l[pos[i]];
    value[i] = i;
    add(value[i], pos[i], 1);
  }
  for (int i = 1, op, k, a, b; i <= m; i++) {
    cin >> op;
    if (op == 1) {
      cin >> a >> b;
      add(value[a], pos[a], -1), add(value[b], pos[b], -1);
      swap(pos[a], pos[b]);
      add(value[a], pos[a], 1), add(value[b], pos[b], 1);
    }
    else {
      cin >> str >> k >> a >> b;
      int rt = 1;
      for (int j = 0; j < k; j++) {
        if (!trie[rt][str[j] - 'a']) {
          rt = -1;
          break;
        }
        rt = trie[rt][str[j] - 'a'];
      }
      if (rt == -1) {
        cout << "0\n";
      }
      else {
        cout << get_sum(a, b, l[rt], r[rt]) << "\n";
      }
    }
  }
  return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值