• CF163E e-Government


    题目地址


    题解

    很明显那 (k) 个字符串作为模式串,建 AC 自动机。考虑每个字符串在当前动态维护的集合中出现多少次,由于总长度 $ le 10^6$,让匹配串在 AC 自动机上走,显然这个后缀出现的次数就是他在 fail 树上的子树的节点的 (cnt)

    我们需要一个树上数据结构,动态维护:

    • 单点修改
    • 链求和

    考虑维护每个点的练求和值,每个单点修改的影响是他子树的答案 (pm 1),所以问题转化为:

    • 子树修改
    • 单点求和

    (dfs) 序 + 差分树状数组,搞定。

    代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    const int N = 1e6 + 5;
    typedef long long LL;
    
    int m, n, match[N], tr[N][26], fail[N], idx;
    int q[N], sz[N], c[N], dfn[N], cnt[N], dfncnt;
    char s[N];
    
    int head[N], numE = 0;
    struct E{
    	int next, v;
    } e[N];
    
    void add(int u, int v) {
    	e[++numE] = (E) { head[u], v };
    	head[u] = numE;
    }
    
    void insert(int id) {
    	int p = 0;
    	for (int i = 1; s[i]; i++) {
    		int ch = s[i] - 'a';
    		if (!tr[p][ch]) tr[p][ch] = ++idx;
    		p = tr[p][ch];
    	}
    	match[id] = p;
    }
    
    void build() {
    	int hh = 0, tt = -1;
    	for (int i = 0; i < 26; i++)
    		if (tr[0][i]) q[++tt] = tr[0][i];
    	while (hh <= tt) {
    		int u = q[hh++];
    		for (int i = 0; i < 26; i++) {
    			int v = tr[u][i];
    			if (v) {
    				fail[v] = tr[fail[u]][i];
    				q[++tt] = v;
    			} else tr[u][i] = tr[fail[u]][i];
    		}
    	}
    	for (int i = 1; i <= idx; i++) add(fail[i], i);
    }
    
    void dfs(int u) {
    	dfn[u] = ++dfncnt, sz[u] = 1;
    	for (int i = head[u]; i; i = e[i].next) {
    		int v = e[i].v;
    		dfs(v);
    		sz[u] += sz[v];
    	}
    }
    
    void insert(int x, int k) {
    	for (; x <= dfncnt; x += x & -x) c[x] += k;
    }
    
    int ask(int x) {
    	int res = 0;
    	for (; x; x -= x & -x) res += c[x];
    	return res;
    }
    
    int main() {
    	scanf("%d%d", &m, &n);
    	for (int i = 1; i <= n; i++) {
    		scanf("%s", s + 1);
    		insert(i);
    	}
    	build();
    	dfs(0);
    	for (int i = 1; i <= n; i++) {
    	    insert(dfn[match[i]], 1);
    		insert(dfn[match[i]] + sz[match[i]], -1);
    		cnt[i] = 1;
    	}
    	while (m--) {
    		char op; cin >> op;
    		if (op == '?') {
    			scanf("%s", s + 1);
    			int p = 0; LL ans = 0;
    			for (int i = 1; s[i]; i++) {
    				int ch = s[i] - 'a';
    				p = tr[p][ch];
    				ans += ask(dfn[p]);
    			}
    			printf("%lld
    ", ans);
    		} else {
    			int x; scanf("%d", &x);
    			if ((op == '+' && cnt[x]) || (op == '-' && !cnt[x])) continue;
    			cnt[x] = op == '+' ? 1 : 0;
    			insert(dfn[match[x]], op == '+' ? 1 : -1);
    			insert(dfn[match[x]] + sz[match[x]], op == '+' ? -1 : 1);
    		} 
    	}
    	return 0;
    }
    
  • 相关阅读:
    Visual Studio 2019 使用 Web Deploy 发布远程站点到IIS服务器
    postman下载地址
    ASP.NET Core开发-Docker部署运行
    C# ffmpeg 视频处理格式转换具体案例
    C# ffmpeg 视频处理格式转换和添加水印
    C# ffmpeg 视频处理
    Tomcat 安装与配置
    Maven 快速入门
    Jenkins 快速搭建
    Google SRE 读书笔记 扒一扒SRE用的那些工具
  • 原文地址:https://www.cnblogs.com/dmoransky/p/12442970.html
Copyright © 2020-2023  润新知