首页 > 其他分享 >P2414 [NOI2011] 阿狸的打字机

P2414 [NOI2011] 阿狸的打字机

时间:2024-09-20 18:34:35浏览次数:1  
标签:26 P2414 idx 阿狸 int son NOI2011 str fail

题目

image

思路

将每一个输出的串放入一个 Trie 树中。

考虑离线处理询问 \((x, y)\),对于每一个 \(y\) 集中处理所有的 \(x\),\(y\) 在 Trie 树上走,走过的点标记一下,结果就是 \(x\) 字符串结尾节点在 fail 树上的对应节点的子树的标记数量。

记得在节点离开的时候撤销标记。

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 100010, M = 2600010;

struct edge {
    int to, next;
} e[M];

int head[M], idx = 1;

void add(int u, int v) {
    idx++;
    e[idx].to = v;
    e[idx].next = head[u];
    head[u] = idx;
}

string opt_str;
int q, ans[N];
vector<pair<int, int> > query[N];

struct ac {
    int son[26];
    int fa;
    int fail;
} t[N];

int ac_idx;
int val[N];

void insert() {
    int p = 0, str_idx = 0;
    for (auto x : opt_str) {
        if (x >= 'a' && x <= 'z') {
            int u = x - 'a';
            if (!t[p].son[u]) t[p].son[u] = ++ac_idx;
            t[t[p].son[u]].fa = p;
            p = t[p].son[u];
        }
        else if (x == 'B') p = t[p].fa;
        else {
            str_idx++;
            val[str_idx] = p;
        }
    }
}

void getfail() {
    queue<int> q;

    for (int i = 0; i < 26; i++) {
        if (t[0].son[i]) {
            q.push(t[0].son[i]);
        }
    }

    while (q.size()) {
        int g = q.front();
        q.pop();

        for (int i = 0; i < 26; i++) {
            if (t[g].son[i]) {
                t[t[g].son[i]].fail = t[t[g].fail].son[i];
                q.push(t[g].son[i]);
            }
            else t[g].son[i] = t[t[g].fail].son[i];
        }
    }
    for (int i = 1; i <= ac_idx; i++) add(t[i].fail, i);
}

int dfn[N], sz[N], dfn_idx;

void dfs(int u, int fa) {
    sz[u] = 1, dfn[u] = ++dfn_idx;
    for (int i = head[u]; i; i = e[i].next) {
        int to = e[i].to;
        if (to == fa) continue;
        dfs(to, u);
        sz[u] += sz[to];
    }
}

int tr[N];

void modify(int u, int x) {
    if (u <= 0) return;
    for (; u < N; u += u & -u) {
        tr[u] += x;
    }
}

int ask(int u) {
    if (u <= 0) return 0;
    int ans = 0;
    for (; u; u -= u & -u) ans += tr[u];
    return ans;
}

void getans() {
    int p = 0, str_idx = 0;
    for (auto x : opt_str) {
        if (x >= 'a' && x <= 'z') {
            int u = x - 'a';
            p = t[p].son[u];
            modify(dfn[p], 1);
        }
        else if (x == 'B') {
            modify(dfn[p], -1);
            p = t[p].fa;
        }
        else {
            str_idx++;
            for (auto [x, y] : query[str_idx]) {
                int ver = val[x];
                ans[y] = ask(dfn[ver] + sz[ver] - 1) - ask(dfn[ver] - 1);
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> opt_str >> q;
    for (int i = 1; i <= q; i++) {
        int l, r;
        cin >> l >> r;
        query[r].push_back({l, i});
    }

    insert();
    getfail();
    dfs(0, 0);
    getans();

    for (int i = 1; i <= q; i++) cout << ans[i] << '\n';
    return 0;
}

标签:26,P2414,idx,阿狸,int,son,NOI2011,str,fail
From: https://www.cnblogs.com/Yuan-Jiawei/p/18423042

相关文章

  • 阿狸教你怎么恢复微信数据
    大家好我是零距离工作室主理阿狸,最近工作室接到很多订单,大多都是关于微信数据恢复的,而且尤其以恢复微信聊天记录为多。在当今这个世界上,微信已然成为我们日常生活中不可或缺的一款软件。它在我们的社交、工作以及各种日常交流场景中都发挥着至关重要的作用,无论是与亲朋好友保持......
  • [Ynoi2011] 初始化
    题目链接:[Ynoi2011]初始化神仙trick+卡常题,前缀后缀和根本没听过。根号分治+分块。对于修改操作,发现是跳着修改,考虑根号分治。若\(x\ge\sqrt{n}\),直接暴力更改,复杂度\(O(\sqrt{n})\)。反之,可以将序列抽象成一堆大小为\(x\)的段,如图,\(l,r\)是查询的区间。发现\(......
  • P5311 [Ynoi2011] 成都七中
    题目描述给你一棵\(n\)个节点的树,每个节点有一种颜色,有\(m\)次查询操作。查询操作给定参数\(l\r\x\),需输出:将树中编号在\([l,r]\)内的所有节点保留,\(x\)所在连通块中颜色种类数。每次查询操作独立。对于\(100\%\)的数据,所有出现过的数在\([1,10^5]\)之间,保证每......
  • P1971 [NOI2011] 兔兔与蛋蛋游戏 题解
    Description这些天,兔兔和蛋蛋喜欢上了一种新的棋类游戏。这个游戏是在一个\(n\)行\(m\)列的棋盘上进行的。游戏开始之前,棋盘上有一个格子是空的,其它的格子中都放置了一枚棋子,棋子或者是黑色,或者是白色。每一局游戏总是兔兔先操作,之后双方轮流操作,具体操作为:兔兔每次操作......
  • P5311 Ynoi2011 成都七中
    P5311Ynoi2011成都七中点分树好题,太妙了。思路看到树和连通块,考虑点分树。但是从这里发现原树和点分树的联系实在太小,貌似不可做。可以发现对于一个询问,一个点如果和\(x\)在一个连通块内,那么这个点到\(x\)的最大最小节点编号肯定都在\([l,r]\)这个范围内。可以证明,......
  • P3214 [HNOI2011] 卡农
    整理下题目的三个条件:选出的\(m\)个集合都不为空。不存在完全相同的两个集合。元素\(1,2,\dots,n\)在所有的集合出现的次数均为偶数。首先,计算有序的集合是相对容易的,只需最后除以\(m!\)即可。记\(f_{i}\)表示考虑前\(i\)个集合满足以上三个条件的方案数。从条......
  • 卡农 -- HNOI2011 -- DP&组合
    卡农--\(HNOI2011\)$$luogu$$$$HZOI$$题意给定一个集合$A={1\lex\len|x}$,求出其\(m\)个不相同的且不为空集的子集,使得第\(i\)个元素的在所有选出的子集中出现的次数\(appear_i\mod2=0\)题解首先一个已知结论:对于一个\(A\)这样的集合,他......
  • #根号分治,分块#洛谷 5309 [Ynoi2011] 初始化
    题目传送门分析如果\(x\)比较大那么可以暴力修改,\(x\)比较小的话可以用数组打标记查询的时候对于暴力修改的部分可以分块,暴力修改的同时需要给块打标记如果\(x\)比较小的情况,一段区间相当于是中间很多段周期加上前后缀(当然可以直接区间减但是我被卡常了)我调的块长是160......
  • [NOI2011] 阿狸的打字机
    把询问全部放到树上,离线dfs处理点击查看代码#include<bits/stdc++.h>usingnamespacestd;strings;intt[100005][26],tot,cnt,r[100005],fa[100005],fail[100005],dfn[100005],w[100005],sum,ans[100005];boolb[100005];vector<int>a[100005];vector<int>o[1000......
  • 洛谷 P5311 [Ynoi2011] 成都七中
    洛谷传送门转化一下题意,变成求\(x\)在只经过编号\(\in[l,r]\)的点,能走到多少种颜色。考虑建出点分树。一个结论是原树上的一个连通块,一定存在一个点,使得它在点分树上的子树完全包含这个连通块的所有点。证明考虑点分治的过程,一个连通块如果没被其中一个点剖开就一定在同一......