zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    使用牛顿迭代法和二分法求解一个数的平方根(python语言实现)
    厄拉多塞筛法和普通方法求素数表(python实现)
    使用辗转相除法求两个数的最大公因数(python实现)
    我在博客园第一篇博文
    Linux安装maven
    MyBatis基础入门
    Maven的使用入门
    nginx的简单使用和使用nginx在windows上搭建tomcat集群
    后端程序员如何玩转AJAX
    Servlet3.0文件上传
  • 原文地址:https://www.cnblogs.com/LZA119/p/14279479.html
Copyright © 2011-2022 走看看