JZOJ 6943. 【2020.01.05冬令营模拟】社会实践(线段树)

JZOJ 6943. 【2020.01.05冬令营模拟】社会实践

题解

  • 题目的原型还是汉诺塔问题,操作规则和普通汉诺塔问题是一样的,先考虑对于某个单独询问如何计算最优答案。
  • 按普通汉诺塔,考虑还原的过程,发现根本不知道下一步该如何移动,无法解决,
  • 但如果从还原好的状态倒推,每一步移动都是自然的,初始所有圆盘都在最大圆盘应回到的柱子,然后从大到小,如果第 i i i个已复位则忽略, 否则将剩下 i − 1 i-1 i1个移到第三根柱,再移动第 i i i个,步数为 2 i − 1 2^{i-1} 2i1。这样移动可以保证时刻都是合法的。
  • 感觉这种过程很像可以用什么东西一段一段地维护,既然需要维护修改,那么想到线段树。
  • 因为初始位置不确定,所以每个位置需要记录当前段从任意一个柱子开始的最终步数和终止位置,合并时找到右儿子的终止位置作为左儿子的起点来查找即可。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 300010
#define ll long long
#define md 998244353
int a[N], e[N];
struct node {
	ll s, x;
}f[N * 4][4];
void make(int v, int l, int r) {
	if(l == r) {
		f[v][1].x = 6 - a[l] - 1, f[v][2].x = 6 - a[l] - 2, f[v][3].x = 6 - a[l] - 3;
		f[v][a[l]].x = a[l];
		f[v][1].s = f[v][2].s = f[v][3].s = 1;
		f[v][a[l]].s = 0;
	}
	else {
		int mid = (l + r) / 2;
		make(v * 2, l, mid), make(v * 2 + 1, mid + 1, r);
		for(int i = 1; i < 4; i++) {
			f[v][i].x = f[v * 2][f[v * 2 + 1][i].x].x;
			f[v][i].s = (f[v * 2 + 1][i].s * e[mid - l + 1] + f[v * 2][f[v * 2 + 1][i].x].s) % md;
		}
	}
}
void ins(int v, int l, int r, int x, int c) {
	if(l == r) {
		f[v][1].x = 6 - a[l] - 1, f[v][2].x = 6 - a[l] - 2, f[v][3].x = 6 - a[l] - 3;
		f[v][a[l]].x = a[l];
		f[v][1].s = f[v][2].s = f[v][3].s = 1;
		f[v][a[l]].s = 0;
	}
	else {
		int mid = (l + r) / 2;
		if(x <= mid) ins(v * 2, l, mid, x, c); else ins(v * 2 + 1, mid + 1, r, x, c);
		for(int i = 1; i < 4; i++) {
			f[v][i].x = f[v * 2][f[v * 2 + 1][i].x].x;
			f[v][i].s = (f[v * 2 + 1][i].s * e[mid - l + 1] + f[v * 2][f[v * 2 + 1][i].x].s) % md;
		}
	}
}
node find(int v, int l, int r, int x, int y, int c) {
	if(l == x && r == y) return f[v][c];
	int mid = (l + r) / 2;
	if(y <= mid) return find(v * 2, l, mid, x, y, c);
	if(x > mid) return find(v * 2 + 1, mid + 1, r, x, y, c);
	node t = find(v * 2 + 1, mid + 1, r, mid + 1, y, c);
	node t1 = find(v * 2, l, mid, x, mid, t.x);
	return {(t.s * e[mid - x + 1] + t1.s) % md, t1.x}; 
}
int main() {
	int n, Q, i;
	scanf("%d%d", &n, &Q);
	for(i = 1; i <= n; i++) scanf("%d", &a[i]);
	e[0] = 1;
	for(i = 1; i <= n; i++) e[i] = e[i - 1] * 2 % md;
	make(1, 1, n);
	while(Q--) {
		int op, x, y;
		scanf("%d%d%d", &op, &x, &y);
		if(op == 1) {
			a[x] = y;
			ins(1, 1, n, x, y);
		}
		else {
			printf("%lld
", find(1, 1, n, x, y, a[y]).s);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/LZA119/p/14279479.html