zoukankan      html  css  js  c++  java
  • LuoGu:SP1716 GSS3

    Pre

    太棒了,今天早上的努力总算没有白费。

    顺便说一句,本文内容简单,只是动态(DP)的基本原理(而且还不全),神犇请避让,否则会浪费时间。

    原来矩阵乘法还有这么奇葩的坑(还是我太大意了),我是小看它了(话说之前有一次考试我也是用的线段树维护矩阵乘法,貌似当时代码与自己的本意不符,但是恰好是这个错误让程序违背了我的本意,最后(A)了,我真幸运!!!^_^)

    Solution

    动态(DP)的基本原理我就不推导了,主要提醒一下坑,在(Conclusion)里面,这里说一下我构造的矩阵。

    [left[ egin{matrix} 0 & v_i & v_i\ -infty & v_i & v_i\ -infty & -infty & 0 end{matrix} ight] ]

    用来计算答案的矩阵是

    [left[ egin{matrix} f_{i-1}\ g_{i-1}\ 0 end{matrix} ight] ]

    其中的(f_i)表示在范围([0,i])中的最大子段和(准确的说是从询问操作的开头到(i)的最大字段和)。

    其中的(g_i)表示强制性以(i)为结尾,并且开始地方不得小于询问操作的左端点的最大子段和。

    用上面的乘以下面的。

    Code

    #include<bits/stdc++.h>
    #define xx first
    #define yy second
    using namespace std;
    
    const int N = 50000 + 5;
    
    struct Matrix {
    	int info[4][4];
    	Matrix () {
    		for (int i = 1; i <= 3; ++i) {
    			for (int j = 1; j <= 3; ++j) {
    				info[i][j] = INT_MIN;
    			}
    		}
    	}
    	inline void init (int u, int v, int w) {
    		info[1][1] = u;
    		info[2][1] = v;
    		info[3][1] = w;
    		for (int i = 1; i <= 3; ++i) {
    			info[i][3] = info[i][2] = info[i][1];
    		}
    	}
    	inline void stt (int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) {
    		info[1][1] = a1;
    		info[1][2] = a2;
    		info[1][3] = a3;
    		info[2][1] = a4;
    		info[2][2] = a5;
    		info[2][3] = a6;
    		info[3][1] = a7;
    		info[3][2] = a8;
    		info[3][3] = a9;
    	}
    	Matrix operator * (Matrix x) {
    		Matrix res;
    		for (int k = 1; k <= 3; ++k) {
    			for (int j = 1; j <= 3; ++j) {
    				for (int i = 1; i <= 3; ++i) {
    					if (info[i][k] == INT_MIN || x.info[k][j] == INT_MIN) {
    						continue;
    					}
    					res.info [i][j] = max (res.info[i][j], info[i][k] + x.info[k][j]);
    				}
    			}
    		}
    		return res;
    	}
    	inline void print () {
    		for (int i = 1; i <= 3; ++i) {
    			for (int j = 1; j <= 3; ++j) {
    				printf ("%d ", info[i][j]);
    			}
    			printf ("
    ");
    		}
    	}
    }st;
    
    int v[N], n, m;
    
    struct Tree {
    	Matrix sum[N << 1];
    	int lson[N << 1], rson[N << 1], l[N << 1], r[N << 1], tot;
    	inline void push_up (int u) {
    		sum[u] = sum[rson[u]] * sum[lson[u]];
    	}
    	inline void build (int fr, int to) {
    		tot++;
    		int now = tot;
    		l[now] = fr;
    		r[now] = to;
    		if (fr == to) {
    			sum[now].stt (0, v[fr], v[fr], INT_MIN, v[fr], v[fr], INT_MIN, INT_MIN, 0);
    			return ;
    		}
    		int mid = (fr + to) / 2;
    		lson[now] = tot + 1;
    		build (fr, mid);
    		rson[now] = tot + 1;
    		build (mid + 1, to);
    		push_up (now);
    	}
    	inline void update (int fr, int p) {
    		if (l[p] == r[p]) {
    			sum[p].stt (0, v[fr], v[fr], INT_MIN, v[fr], v[fr], INT_MIN, INT_MIN, 0);
    			return ;
    		}
    		int mid = (l[p] + r[p]) / 2;
    		if (fr <= mid) {
    			update (fr, lson[p]);
    		}
    		else {
    			update (fr, rson[p]);
    		}
    		push_up (p);
    	}
    	Matrix query (int fr, int to, int p) {
    		if (fr <= l[p] && to >= r[p]) {
    			return sum[p];
    		}
    		int mid = (l[p] + r[p]) / 2;
    		if (to <= mid) {
    			return query (fr, to, lson[p]);
    		}
    		else if (fr > mid) {
    			return query (fr, to, rson[p]);
    		}
    		else {
    			return query (fr, to, rson[p]) * query (fr, to, lson[p]);
    		}
    	}
    }tree;
    
    inline void solve (int x, int y) {
    	if (x == y) {
    		printf ("%d
    ", v[x]);
    		return ;
    	}
    	else {
    		Matrix st;
    		st.init (v[x], v[x], 0);
    		Matrix tmp = tree.query (x + 1, y, 1);
    		Matrix ans = tmp * st;
    		printf ("%d
    ", ans.info[1][1]);
    	}
    }
    
    int main () {
    	scanf ("%d", &n);
    	for (int i = 1; i <= n; ++i) {
    		scanf ("%d", &v[i]);
    	}
    	tree.build (1, n);
    	int m;
    	scanf ("%d", &m);
    	for (int i = 1; i <= m; ++i) {
    		int opt, x, y;
    		scanf ("%d%d%d", &opt, &x, &y);
    		if (!opt) {
    			v[x] = y;
    			tree.update (x, 1);
    		}
    		else {
    			solve (x, y);
    		}
    	}
    	return 0;
    }
    

    Conclusion


    具体来讲一下我觉得坑的地方。

    如果需要进行一次转移,那么

    假设(M_a)表示位置在(a)(DP)矩阵,(st)表示转移矩阵。

    那么计算(st)(i-1)转移到(i+1)顺序

    1、(st=M_i*st)

    2、(st=M_{i+1}*st)

    也就是

    (st=M_{i+1}*(M_i*st))

    于是我就直接线段树维护区间了。

    但是实际上展开之后是(st=M_{i+1}*M_i*st)

    考虑到矩阵乘法没有交换律,我们需要在(push\_up)(query)里面交换左右儿子的顺序。

    也就是倒着乘。


    总之,注意要把式子看清楚再打代码。。。

  • 相关阅读:
    C#委托本质探索 四、方法变量内、外混合调用
    calibredrv 对layer做操作
    2021年11月工作笔记
    2022年1月工作资料
    2021年12月工作资料
    MySQL 5.7 MGR原理及部署
    在虚拟机上安装redis集群,redis使用版本为4.0.5,本机通过命令客户端可以连接访问,外部主机一直访问不了
    mongoDB中的的常用语法
    使用Nginx做图片服务器时候,配置之后图片访问一直是 404问题解决
    jquery.cookie() 方法的使用(读取、写入、删除)
  • 原文地址:https://www.cnblogs.com/ChiTongZ/p/11180422.html
Copyright © 2011-2022 走看看