[BZOJ2746][HEOI2012]旅行问题(AC自动机+LCA)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=2746

Solution

先介绍下 fail 树:
将 AC 自动机上每个点 u u 的 fail 指针指向的点作为 u 的父亲,构成的树就是 fail 树。
应用:如果 fail 树上 u u v 的祖先,那么 u u 代表的字符串是 v 代表的字符串的前缀兼后缀。
而题意就是询问两个前缀的一个最长的公共后缀 S S <script type="math/tex" id="MathJax-Element-602">S</script> ,满足 S 是给定串之一的前缀
结合 fail 树的性质和定义分析,可以发现此题可以转化为求 fail 树上两点的 LCA 。
套用 LCA 的模板即可通过此题。

Code

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 1e6 + 5, PYZ = 1e9 + 7, LogN = 22, E = 26;
int n, m, QAQ, len, que[N];
char s[N];
struct cyx {
    int son[E], dep, fa[LogN], code;
    void init() {
        dep = code = 0;
        memset(son, 0, sizeof(son));
        memset(fa, 0, sizeof(fa));
    }
} T[N];
vector<int> sp[N];
void ins(int id) {
    int i, u = 1, n = strlen(s + 1);
    For (i, 1, n) {
        int c = s[i] - 'a';
        if (!T[u].son[c]) T[T[u].son[c] = ++QAQ].init();
        T[T[u].son[c]].code = (26ll * T[u].code % PYZ + c) % PYZ;
        u = T[u].son[c]; sp[id].push_back(u);
    }
}
void cyxisdalao() {
    int i, c; T[0].init();
    For (c, 0, 25) T[0].son[c] = 1;
    que[len = 1] = 1; For (i, 1, len) {
        int u = que[i]; For (c, 0, 25)
            if (!T[u].son[c]) T[u].son[c] = T[T[u].fa[0]].son[c];
            else T[T[u].son[c]].fa[0] = T[T[u].fa[0]].son[c],
                T[T[u].son[c]].dep = T[T[T[u].son[c]].fa[0]].dep + 1,
                que[++len] = T[u].son[c];
        For (c, 0, 19) T[u].fa[c + 1] = T[T[u].fa[c]].fa[c];
    }
}
int lca(int u, int v) {
    int i; if (T[u].dep < T[v].dep) swap(u, v);
    Rof (i, 20, 0) {
        if (T[T[u].fa[i]].dep >= T[v].dep) u = T[u].fa[i];
        if (u == v) return u;
    }
    Rof (i, 20, 0) if (T[u].fa[i] != T[v].fa[i])
        u = T[u].fa[i], v = T[v].fa[i];
    return T[u].fa[0];
}
int main() {
    T[QAQ = 1].init(); T[1].dep = 1;
    int i, x, y, a, b; n = read();
    For (i, 1, n) scanf("%s", s + 1), ins(i);
    cyxisdalao(); m = read(); while (m--) {
        x = read(); y = read(); a = read(); b = read();
        printf("%d\n", T[lca(sp[x][y - 1], sp[a][b - 1])].code);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值