HDU 4125 Moles 二叉排序树 树状数组 kmp

9 篇文章 0 订阅
9 篇文章 0 订阅

题目链接

题意

将一串数( n1e6 )依次插入到一棵二叉排序树中, dfs 一遍,将经过的每个节点的信息加到一个串尾(如果当前节点为奇数则加 1 否则加 0 )。最后再给一个模式串,问其在得到的串中出现了多少次。

思路

这道题的每一块都十分清晰,建树, dfs kmp

然而问题就出在了数据量上。因此,要在两个方面进行优化。

1 . 插入到二叉排序树中:

这里有用到一个性质,那就是插入的节点的父亲,或者是大于它的最小值,或者是小于它的最大值,并且有且只有一个合法。
于是问题就转化成了树状数组求第k大元素(先暂时写了个二分的两个 log ,明天再去翻翻看之前的博客写过的一个 log 的算法补上= =还看到有人写笛卡尔树和线段树明天再看情况补)。

2 . dfs

需要手写栈。

Code

#include <bits/stdc++.h>
#define maxn 600010
using namespace std;
typedef long long LL;
int id[maxn], ch[maxn][2], tot, a[maxn], f[7010], c[maxn], n, len, top, st[maxn];
char s[maxn * 3], P[7010];
bool vis[maxn];
void getfail() {
    int m = strlen(P);
    f[0] = f[1] = 0;
    for (int i = 1; i < m; ++i) {
        int j = f[i];
        while (j && P[j] != P[i]) j = f[j];
        f[i+1] = P[i] == P[j] ? j+1 : 0;
    }
}
int kmp() {
    int j = 0, l = len, m = strlen(P), cnt = 0;
    for (int i = 0; i < l; ++i) {
        while (j && s[i] != P[j]) j = f[j];
        if (s[i] == P[j]) ++j;
        if (j == m) ++cnt, j = f[j];
    }
    return cnt;
}
int kas;
void init() {
    memset(c, 0, sizeof(c));
    memset(ch, 0, sizeof ch);
}
int lowbit(int x) { return x & -x; }
void add(int x) { while (x <= n) ++c[x], x += lowbit(x); }
int sum(int x) { int ret = 0; while (x) ret += c[x], x -= lowbit(x); return ret; }
int query(int k) {
    int l = 1, r = n;
    while (r > l) {
        int mid = l + r >> 1;
        if (sum(mid) >= k) r = mid;
        else l = mid+1;
    }
    return l;
}
void insert(int x, int n) {
    add(x);
    int k = sum(x), l, r;
    if (k == 1) l = query(k+1), ch[l][0] = x;
    else if (k == n) r = query(k-1), ch[r][1] = x;
    else {
        l = query(k+1), r = query(k-1);
        if (!ch[r][1]) ch[r][1] = x;
        else ch[l][0] = x;
    }
}
void dfs(int u) {
    memset(vis, 0, sizeof(vis)); top = len = 0;
    st[top++] = u;
    while (top > 0) {
        int v = st[top-1];
        s[len++] = v & 1 ? '1' : '0';
        if (ch[v][0] && !vis[ch[v][0]]) { st[top++] = ch[v][0], vis[ch[v][0]] = true; continue; }
        if (ch[v][1] && !vis[ch[v][1]]) { st[top++] = ch[v][1], vis[ch[v][1]] = true; continue; }
        --top;
    }
}
void work() {
    init();
    int x;
    scanf("%d", &n);
    scanf("%d", &x);
    add(x); int rt = x;
    for (int i = 2; i <= n; ++i) scanf("%d", &x), insert(x, i);

    dfs(rt);
    scanf("%s", P);
    getfail();
    printf("Case #%d: %d\n", ++kas, kmp());
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值