【LOJ3055】「HNOI2019」JOJO

【题目链接】

  • 点击打开链接

【思路要点】

  • 考虑将字符串缩成若干个相同字符的段 ( c n t , c h a r ) (cnt,char) (cnt,char) ,相邻的字符不相同。
  • 考虑一个与某一个前缀匹配的后缀,若该后缀横跨了 x   ( x ≥ 2 ) x\ (x\geq2) x (x2) 段,那么其对应的第 2 2 2 段至第 x − 1 x-1 x1 段的 ( c n t , c h a r ) (cnt,char) (cnt,char) 都应该对应相等;且后缀的第 1 1 1 段与前缀的第 1 1 1 段的 c h a r char char 相等,后缀的 c n t cnt cnt 不小于前缀的 c n t cnt cnt ;后缀的第 x x x 段与前缀的第 x x x 段的 c h a r char char 相等,前缀的 c n t cnt cnt 不小于后缀的 c n t cnt cnt
  • 离线将字符树建出来,然后求出各个点的 f a i l / n e x t fail/next fail/next ,注意这里的匹配允许当前匹配的第 1 1 1 段的 c n t cnt cnt 不小于前缀第 1 1 1 段的 c n t cnt cnt ,具体实现时可以使用可持久化线段树实现回退。
  • 接下来求解答案,我们需要计算新产生的前缀 n e w new new 对答案的贡献。当前节点 c u r cur cur f a i l fail fail 树上的每一个节点 f a fa fa 都存在后继字符 c h a r s o n f a char_{son_{fa}} charsonfa 及其长度 c n t s o n f a cnt_{son_{fa}} cntsonfa ,若 c h a r s o n f a = c h a r n e w char_{son_{fa}}=char_{new} charsonfa=charnew ,则对于每一个 1 ≤ i ≤ M i n { c n t s o n f a , c n t n e w } 1\leq i\leq Min\{cnt_{son_{fa}},cnt_{new}\} 1iMin{cntsonfa,cntnew} ,新产生的第 i i i 个前缀将存在一个长度为 d e p t h f a + i depth_{fa}+i depthfa+i 的匹配。且由于 d e p t h f a depth_{fa} depthfa 是递增的,可以将其看做一次区间赋值操作,用可持久化线段树维护各个字符的各个长度的匹配以实现回退。
  • 注意特殊处理匹配没有横跨 2 2 2 段的情况。
  • 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN)

【代码】

#include
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXP = 5e6 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct SegmentTreeWithInfo {
	struct Node {
		int lc, rc;
		int val;
	} a[MAXP];
	int root, size, n;
	void init(int x) {
		n = x;
		root = size = 0;
	}
	int modify(int root, int l, int r, int pos, int d) {
		int ans = ++size;
		a[ans] = a[root];
		if (l == r) {
			a[ans].val = d;
			return ans;
		}
		int mid = (l + r) / 2;
		if (mid >= pos) a[ans].lc = modify(a[root].lc, l, mid, pos, d);
		else a[ans].rc = modify(a[root].rc, mid + 1, r, pos, d);
		return ans;
	}
	int modify(int root, int pos, int d) {
		return modify(root, 1, n, pos, d);
	}
	int query(int root, int l, int r, int pos) {
		if (l == r) return a[root].val;
		int mid = (l + r) / 2;
		if (mid >= pos) return query(a[root].lc, l, mid, pos);
		else return query(a[root].rc, mid + 1, r, pos);
	}
	int query(int root, int pos) {
		return query(root, 1, n, pos);
	}
} Edge;
struct SegmentTreeWithAnswer {
	struct Node {
		int lc, rc;
		int tag; ll sum;
	} a[MAXP];
	int root, size, n;
	void init(int x) {
		n = x;
		root = size = 0;
	}
	void update(int root) {
		a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum;
	}
	void pushdown(int root, int l, int r) {
		if (a[root].tag) {
			int mid = (l + r) / 2;
			int lc = ++size;
			a[lc] = a[a[root].lc];
			a[lc].tag = a[root].tag;
			a[lc].sum = 1ll * (mid - l + 1) * a[root].tag;
			a[root].lc = lc;
			int rc = ++size;
			a[rc] = a[a[root].rc];
			a[rc].tag = a[root].tag;
			a[rc].sum = 1ll * (r - mid) * a[root].tag;
			a[root].rc = rc;
			a[root].tag = 0;
		}
	}
	int modify(int root, int l, int r, int ql, int qr, int d) {
		int ans = ++size;
		a[ans] = a[root];
		if (l == ql && r == qr) {
			a[ans].tag = d;
			a[ans].sum = (r - l + 1ll) * d;
			return ans;
		}
		pushdown(ans, l, r);
		int mid = (l + r) / 2;
		if (mid >= ql) a[ans].lc = modify(a[ans].lc, l, mid, ql, min(mid, qr), d);
		if (mid + 1 <= qr) a[ans].rc = modify(a[ans].rc, mid + 1, r, max(mid + 1, ql), qr, d);
		update(ans);
		return ans;
	}
	int modify(int root, int pos, int d) {
		return modify(root, 1, n, 1, pos, d + 1);
	}
	int getMax(int root, int l, int r) {
		if (a[root].tag) return r;
		int mid = (l + r) / 2;
		if (a[a[root].rc].sum) return getMax(a[root].rc, mid + 1, r);
		else return getMax(a[root].lc, l, mid);
	}
	ll getsum(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].sum;
		if (a[root].tag) return (qr - ql + 1ll) * a[root].tag;
		int mid = (l + r) / 2; ll ans = 0;
		if (mid >= ql) ans += getsum(a[root].lc, l, mid, ql, min(qr, mid));
		if (mid + 1 <= qr) ans += getsum(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
		return ans;
	}
	ll query(int root, int pos, int least) {
		if (a[root].sum == 0) return 0;
		int Max = getMax(root, 1, n); ll ans = 0;
		if (Max < pos) {
			if (least <= Max) ans = 1ll * (pos - Max) * least;
			else if (least >= pos) ans = (Max + 1ll + pos) * (pos - Max) / 2;
			else ans = 1ll * least * (least - Max) + (least + 1ll + pos) * (pos - least) / 2;
		}
		chkmin(pos, Max);
		return ans + getsum(root, 1, n, 1, pos) + pos * (pos - 1ll) / 2;
	}
} ST;
struct AcAutomaton {
	struct Node {
		vector <int> sons, ori;
		int depth, redge, rans, fail;
		char c; ll ans;
	} a[MAXN];
	char key; int first, firstp;
	map <pair <int, char>, int> home;
	int cnte, Max, size, tot, pos[MAXN];
	void update(pair <int, char> x) {
		if (!home.count(x)) home[x] = ++cnte;
	}
	void rebuild(int pos, char c, int fa) {
		for (auto x : a[pos].ori) {
			if (a[x].c == c) {
				a[fa].sons.push_back(x);
				update(make_pair(a[x].depth - a[fa].depth, a[x].c));
				rebuild(x, a[x].c, fa);
			} else {
				a[pos].sons.push_back(x);
				update(make_pair(a[x].depth - a[pos].depth, a[x].c));
				rebuild(x, a[x].c, pos);
			}
		}
	}
	void getfail(int pos) {
		//cerr << pos << ' ' << a[pos].fail << endl;
		for (auto x : a[pos].sons) {
			chkmax(Max, a[x].depth - a[pos].depth);
			a[x].fail = Edge.query(a[a[pos].fail].redge, home[make_pair(a[x].depth - a[pos].depth, a[x].c)]);
			if (a[x].fail == 0 && a[x].c == key && a[x].depth - a[pos].depth >= first) a[x].fail = firstp;
			a[x].redge = a[a[x].fail].redge;
			int bak = a[pos].redge;
			a[pos].redge = Edge.modify(a[pos].redge, home[make_pair(a[x].depth - a[pos].depth, a[x].c)], x);
			if (pos == 0) key = a[x].c, first = a[x].depth - a[pos].depth, firstp = x;
			getfail(x);
			a[pos].redge = bak;
		}
	}
	ll func(int x) {
		return x * (x - 1ll) / 2;
	}
	void getans(int pos) {
		for (auto x : a[pos].sons) {
			int troot = Edge.query(a[a[pos].fail].redge, a[x].c - 'a' + 1);
			if (pos == 0) {
				a[x].ans = a[pos].ans + func(a[x].depth - a[pos].depth);
				key = a[x].c, first = a[x].depth - a[pos].depth;
			} else a[x].ans = a[pos].ans + ST.query(troot, a[x].depth - a[pos].depth, first * (a[x].c == key));
			a[x].redge = a[a[x].fail].redge;
			int bak = a[pos].redge; troot = Edge.query(a[pos].redge, a[x].c - 'a' + 1);
			troot = ST.modify(troot, a[x].depth - a[pos].depth, a[pos].depth);
			a[pos].redge = Edge.modify(a[pos].redge, a[x].c - 'a' + 1, troot);
			getans(x);
			a[pos].redge = bak;
		}
	}
	void work() {
		rebuild(0, -1, 0);
		Edge.init(cnte);
		getfail(0);
		Edge.init(26);
		ST.init(Max);
		getans(0);
		for (int i = 1; i <= tot; i++)
			writeln(a[pos[i]].ans % 998244353);
	}
	void extend(int x, char c) {
		pos[++tot] = ++size;
		a[size].c = c;
		a[size].depth = a[pos[tot - 1]].depth + x;
		a[pos[tot - 1]].ori.push_back(size);
	}
	void rev(int x) {
		pos[++tot] = pos[x];
	}
} ACAM;
int main() {
	int n; read(n);
	for (int i = 1; i <= n; i++) {
		int opt, x; char c;
		read(opt), read(x);
		if (opt == 1) {
			c = getchar();
			ACAM.extend(x, c);
		} else ACAM.rev(x);
	}
	ACAM.work();
	return 0;
}

你可能感兴趣的:(【OJ】LOJ,【类型】做题记录,【数据结构】可持久化数据结构,【数据结构】线段树,【算法】KMP算法)