BJOI2016 IP地址

题目链接

Description

给定 \(n\)\(01\) 模式串。\(q\) 次询问:

每次询问给定一个 \(01\) 串:

  • 设给这个串匹配的串是在模式串中存在的他的最长前缀
  • \([a, b]\) 时刻内,这个匹配的串变了多少次。

Solution

这题很坑没有给出 \(01\) 串的长度数据范围,不过应该能读进来说明是个字符串总数的复杂度是够的。

统计两个串符合某种前缀关系,首先想到 \(\text{Trie}\)

显然这个答案符合区间可加性,设答案 \(=\) 匹配串在前 \(r\) 个时刻的变换次数 \(-\) 匹配串在前 \(l\) 个时刻的变换次数。拆完询问后,不妨考虑离线,考虑从小到大加入一个模式串到 \(\text{Trie}\) 中,然后统计相应位置下的答案。

考虑在 \(Trie\) 树上动态维护每个节点的信息:

  • \(cnt_i\) 表示从开始到当前时刻,如果匹配串在这个节点,它的最长前缀会被修改多少次

比较显然的是,如果存在两个模式串 \(a,b\)\(a\)\(b\) 的前缀。那么当 \(b\) 存在的时候,\(a\) 当前要进行加入 / 删除, \(a\) 的修改不会影响 \(b\) 节点的子树(因为 \(b\) 节点的子树的最长前缀还是 \(b\)),但是会影响 \(a\) 的子树除了 \(b\) 的子树的部分,让他们的 \(cnt\)\(+1\)。这启示我们再弄一个信息:

  • \(st_i\) 表示 \(i\) 这个节点当前有没有模式串

那么每次修改 / 删除一个模式串 \(a\),就把 \(a\)\(\text{Trie}\) 下的从上到下不断扩展,除非遇到 \(st\)\(\text{True}\),那么停止。

然后我们发现这个东西其实和线段树的 \(\text{pushdown}\) 很像,然后再想想是可以这么搞的,弄个加法 \(\text{tag}\) 就行了,所以就 \(\text{OK}\) 了。值得注意的是 \(\text{pushdown}\) 仅能用在祖先全部处理完的情况下,不能胡乱加标记,否则修改的先后顺序会出现混乱,这是我之前尝试的(因为我不懂线段树 QAQ)。

时间复杂度

\(O(线性)\)

Code

#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
using namespace std;

const int N = 100005;

int n, m, tr[N * 64][2], idx, cnt[N * 64];
int add[N * 64], pos[N], ans[N];

bool st[N * 64], opt[N];

char op[4], s[N][64], g[N][64];

vector<int> q[N];


void inline pushdown(int p) {
	if (!add[p]) return;
	if (!tr[p][0]) tr[p][0] = ++idx;
	if (!tr[p][1]) tr[p][1] = ++idx;
	if (!st[tr[p][0]]) add[tr[p][0]] += add[p], cnt[tr[p][0]] += add[p];
	if (!st[tr[p][1]]) add[tr[p][1]] += add[p], cnt[tr[p][1]] += add[p];
	add[p] = 0;
}

void inline insert(int id, bool tag) {
	int p = 0;
	for (int i = 0; s[id][i]; i++) {
		int ch = s[id][i] - '0';
		pushdown(p);
		if (!tr[p][ch]) tr[p][ch] = ++idx;
		p = tr[p][ch];
	}
	cnt[p]++, add[p]++;
	st[p] = tag;
}

int inline query(int id) {
	int p = 0;
	for (int i = 0; g[id][i]; i++) {
		int ch = g[id][i] - '0';
		if (!tr[p][ch]) return cnt[p];
		p = tr[p][ch];
		pushdown(p);
	}
	return cnt[p];
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%s%s", op, s[i]);
		opt[i] = op[0] == 'A';
	}
	for (int i = 1, a, b; i <= m; i++) {
		scanf("%s%d%d", g[i], &a, &b);
		q[a].push_back(i);
		q[b].push_back(i); 
	}
	for (int i = 1; i <= n; i++) {
		insert(i, opt[i]);
		for (int j = 0; j < q[i].size(); j++) {
			int id = q[i][j];
			ans[id] = query(id) - ans[id];
		}
	}
	for (int i = 1; i <= m; i++) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2020-03-31 10:40  DMoRanSky  阅读(274)  评论(0编辑  收藏  举报