• 「NOI 2020」美食家


    「NOI 2020」美食家

    题面

    某谷P6772

    基本思路

    (n leq 50)(w leq 5) 中不难看出,这是一个矩阵快速幂,利用一个拆点的思想,可以先做一下 P4159迷路,然后再用一个二进制拆分,不错,就是 P6190魔法

    将上俩个题的思路综合起来,这个题就好处理多了。

    拆点

    何为拆点,如何拆点?

    首先,我们拿一个图为例:

    当前这个图的01邻接矩阵为:

    (egin{bmatrix} 0&1&1&0 \ 0&0&1&0 \ 0&0&0&0 \ 1&0&1&0 end{bmatrix})

    可以理解为 (a[i][j])(i) 走一步能否到 (j)

    将这个矩阵相乘:

    ( egin{bmatrix} 0&1&1&0 \ 0&0&1&0 \ 0&0&0&0 \ 1&0&1&0 \ end{bmatrix} imes egin{bmatrix} 0&1&1&0 \ 0&0&1&0 \ 0&0&0&0 \ 1&0&1&0 end{bmatrix} = egin{bmatrix} 0&0&1&0 \ 0&0&0&0 \ 0&0&0&0 \ 0&1&1&0 end{bmatrix} )

    易证,得出的矩阵 (a[i][j]) 表示从 (i)(2) 步能否到达 (j)

    不妨设一开始的矩阵为 (base) ,则可以得出:(base^k) 为从 (i)(k) 步能否到达 (j)


    但是这道题还有一个点权,该怎么统计呢?

    我们可以将矩阵中的元素更改,并将矩阵乘法的运算重新定义。

    不妨设 (a[i][j]) 为从 (i)(j) 的最长路,那么矩阵中的运算就改为了:

    c.a[i][j] = max (c.a[i][j], a.a[i][k] + b.a[k][j]);
    

    注意将不能到达的点,权值设为 (-INF),这样无论怎样相加都不会影响到答案了。


    emmmm,好像跑的有点远。

    由于我们的矩阵只支持边权为1时的图,所以我们要拆点。

    拆点,顾名思义,就是将一个点拆成多个点,如下图:

    图中1、6 ,就是我们原来的1和2 。

    如果我们要从2到1建一条边权为3的边,我们就将新图中从6到3建一条边,这样就达到了我们的目的,也可以跑矩阵快速幂了。

    分块快速幂

    如果 (k=0),那么答案很简单就是 (base^T.a[1][1])

    (k eq0),我们可以将整段 (T) 时间分成 (k+1) 块进行矩阵快速幂。

    这样在每次美食节结束的时候,在统计答案的矩阵里,在相应的位置加上额外的贡献即可。

    到现在我们就可以粗略的码一个效率为 (Theta(kn^3log_T)) 的小暴力,显然会T到飞起。

    二进制拆分

    从上面我们会发现,在每次分块快速幂的时候,我们都将统计答案的 (ans) 矩阵乘上了一个 (base^t)

    我们可以现将 (base) 矩阵的 (2^x) 次方预处理出来。

    在统计答案时,将这段时间二进制拆分,乘上相应的 (base^{2^x})

    好像差不多可以优化一个 (log) 的效率,但还是会T到飞起。

    搞掉一个n

    怎么能搞掉一个n呢?

    我们会发现,我们统计答案的矩阵 (ans),只用到了 (i = 1) 的地方,所以我们直接只计算 (i = 1) 的那一行的答案即可。

    搞掉一个n不轻轻松松?

    当前复杂度 (Theta(kn^2logT)),直接碾掉!!!

    代码(自带大常数)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    #define ll long long
    #define DEBUG puts ("emmmm")
    
    using namespace std;
    
    const int maxn = 5e2 + 50;
    
    inline int read () {
    	int x = 0, w = 1;
    	char ch = getchar();
    	for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
    	for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
    	return x * w;
    }
    
    int n, m, T, K;
    int c[maxn];
    
    struct Festival{
    	int t, pos, val;
    	inline bool operator < (const Festival &a) const {
    		return t < a.t;
    	};
    }Fes[maxn];
    
    struct Matrix{
    	ll a[260][260];
    	Matrix () {
    		memset (a, -0x3f, sizeof a); //初始为-INF,对答案不会造成影响
    	};
    }base, ans, basem[50];
    
    inline Matrix Matx (Matrix a, Matrix b) {
    	Matrix ans;
    	register int i = 1, j = 1, k = 1;
    	for (; i <= n * 5; i ++) {
    		for (j = 1; j <= n * 5; j ++) {
    			for (k = 1; k <= n * 5; k ++) {
    				ans.a[i][j] = max (ans.a[i][j], a.a[i][k] + b.a[k][j]);
    			}
    		}
    	}
    	return ans;
    }
    
    inline Matrix Matx0 (Matrix a, Matrix b) {
    	Matrix ans;
    	register int j = 1, k = 1;
    	for (; j <= n * 5; j ++) {
    		for (k = 1; k <= n * 5; k ++) {
    			if (a.a[1][k] != -1 && b.a[k][j] != -1) ans.a[1][j] = max (ans.a[1][j], a.a[1][k] + b.a[k][j]);
    		}
    	}
    	return ans;
    }
    
    inline Matrix Matqpow (Matrix a, register int b) {
    	Matrix ans;
    	register int i = 1;
    	for (; i <= n * 5; i ++) ans.a[i][i] = 1;
    	while (b) {
    		if (b & 1) ans = Matx (ans, a);
    		a = Matx (a, a);
    		b >>= 1;
    	}
    	return ans;
    }
    
    int main () {
    	freopen ("delicacy.in", "r", stdin);
    	freopen ("delicacy.out", "w", stdout);
    	n = read(), m = read(), T = read(), K = read();
    	for (register int i = 1; i <= n; i ++) c[i] = read();
    	for (register int i = 1; i <= n; i ++) {
    		for (register int j = 1; j <= 4; j ++) {
    			register int u = (i - 1) * 5 + j + 1, v = (i - 1) * 5 + j; // 负责的建边,建议自己模拟
    			if (j != 1) base.a[u][v] = 0;
    			else base.a[u][v] = c[i];
    		}
    	}
    	for (register int i = 1; i <= m; i ++) {
    		register int u = read(), v = read(), w = read();
    		if (w != 1) base.a[(u - 1) * 5 + 1][(v - 1) * 5 + w] = 0; // 负责的建边,建议自己模拟
    		else base.a[(u - 1) * 5 + 1][(v - 1) * 5 + w] = c[v];
    	}
    	for (register int i = 1; i <= K; i ++) Fes[i].t = read(), Fes[i].pos = read(), Fes[i].val = read();
    	sort (Fes + 1, Fes + K + 1);
    	Fes[++ K].t = T;
    	basem[1] = base;
    	for (register int i = 2; (1 << i - 1) <= T; i ++) {
    		basem[i] = Matx (basem[i - 1], basem[i - 1]);
    	}
    	register int last = 0;
    	register bool flag = 0;
    	ans.a[1][1] = 0;
    	for (register int i = 1; i <= K; i ++) {
    		register int x = Fes[i].t - last;
    		for (register int j = 1; j <= 30; j ++) {
    			if (1 << j - 1 & x) {
    				ans = Matx0 (ans, basem[j]);
    			}
    		}
    		last = Fes[i].t;
    		for (register int j = 1; j <= n * 5; j ++) {
    			if (ans.a[j][(Fes[i].pos - 1) * 5 + 1] >= 0) {
    				ans.a[j][(Fes[i].pos - 1) * 5 + 1] += Fes[i].val;	
    			}
    		}
    	}
    	if (ans.a[1][1] < 0) {
    		puts ("-1");
    	}else {
    		printf ("%lld
    ", ans.a[1][1] + c[1]); // 别忘了最后加上刚开始获得的愉悦值
    	}
    	return 0;
    }
    
  • 相关阅读:
    Cenots6.5 安装 Intel I219-V 网卡驱动
    nginx的安装
    NGINX 简介
    HTTP 并发响应模型
    日志相关的服务器参数详解:
    使用mysqlbinlog提取二进制日志(zz)
    浅谈mysql日志
    mysql DDL&DML 语言
    fastcgi与cgi的区别(zz)
    基于rancher部署Spring-cloud微服务系统
  • 原文地址:https://www.cnblogs.com/Rubyonly233/p/13731353.html
Copyright © 2020-2023  润新知