zoukankan      html  css  js  c++  java
  • 【题解】 [NOI Online #1 入门组]魔法 最短路+矩阵快速幂+floyd+dp Luogu6189

    Legend

    Link ( extrm{to Luogu})

    C 国由 (n) 座城市与 (m) 条有向道路组成,城市与道路都从 (1) 开始编号,经过 (i) 号道路需要 (t_i) 的费用。

    现在你要从 (1) 号城市出发去 (n) 号城市,你可以施展最多 (k) 次魔法,使得通过下一条道路时,需要的费用变为原来的相反数,即费用从 (t_i) 变为 (-t_i)。请你算一算,你至少要花费多少费用才能完成这次旅程。

    (1 le n le 100,0 le m le frac{n(n-1)}{2},1 le t_i le 10^9,0 le k le 10^6)

    Editorial

    本题十分有意思,为出题人点赞!

    首先我们求出

    • (F[0]_{i,j}) 表示 (i ightarrow j) 使用 (0) 次魔法的最少花费。
    • (F[1]_{i,j}) 表示 (i ightarrow j) 使用 (1) 次魔法的最少花费。

    这个可以 Floyd 直接分层图。

    我们考虑从使用一次魔法推到使用两次魔法:(F[2]_{i,j}=minlimits_{k=1}^{n}F[1]_{i,k}+F[1]_{k,j})

    从两次魔法推到三次 (F[3]_{i,j}= minlimits_{k=1}^{n} F[2]_{i,k}+F[1]_{k,j})

    显然可以直接用矩阵优化这个转移。初始矩阵 (F[0]),转移矩阵 (F[1])

    复杂度 (O(n^3log k))

    Off-topic

    说句题外话,我在写这个题的分层图的时候,最开始一不小心没有建第二层(使用了魔法后)的图。

    我写的是右乘转移,于是就过不了样例。改成了左乘就可以,并且可以 AC(官方数据也可以)。

    这让我感到非常奇怪,我得到的转移矩阵明明都是错的,为什么还能 AC?

    其实根据矩阵的结合律从右往左看,就容易发现这左乘是对的了。

    Code

    #include <bits/stdc++.h>
    
    #define debug(...) fprintf(stderr ,__VA_ARGS__)
    #define LL long long
    
    using namespace std;
    
    const int MX = 200 + 2;
    
    int sz ,n ,m ,K;
    
    void chkmin(LL &a ,LL b){a = std::min(a ,b);}
    struct Matrix{
    	LL A[102][102];
    	Matrix operator *(const Matrix& B)const{
    		Matrix C;
    		for(int i = 1 ; i <= n ; ++i){
    			for(int j = 1 ; j <= n ; ++j){
    				C.A[i][j] = 1LL << 50;
    				for(int k = 1 ; k <= n ; ++k){
    					chkmin(C.A[i][j] ,A[i][k] + B.A[k][j]);
    				//	chkmin(C.A[i][j] ,B.A[i][k] + A[k][j]);
    				}
    			}
    		}
    		return C;
    	}
    	void output(){
    		for(int i = 1 ; i <= n ; ++i){
    			for(int j = 1 ; j <= n ; ++j){
    				debug("%lld%c" ,A[i][j] ," 
    "[j == n]);
    			}
    		}
    		debug("=======================");
    	}
    }org ,tr;
    
    LL dis[MX][MX];
    
    
    
    int main(){
    	cin >> n >> m >> K;
    	memset(dis ,0x3f ,sizeof dis);
    	for(int i = 1 ; i <= 2 * n ; ++i) dis[i][i] = 0;
    	for(int i = 1 ,u ,v ,w ; i <= m ; ++i){
    		cin >> u >> v >> w;
    		chkmin(dis[u][v] ,w);
    		chkmin(dis[u][v + n] ,-w);
    		chkmin(dis[u + n][v + n] ,w);
    	}
    	for(int k = 1 ; k <= 2 * n ; ++k)
    		for(int i = 1 ; i <= 2 * n ; ++i)
    			for(int j = 1 ; j <= 2 * n ; ++j)
    				chkmin(dis[i][j] ,dis[i][k] + dis[k][j]);
    	for(int i = 1 ; i <= n ; ++i){
    		for(int j = 1 ; j <= n ; ++j){
    //			debug("dis[%d][%d] = %lld ,mag = %lld.
    " ,i ,j ,dis[i][j] ,dis[i][j + n]);
    			tr.A[i][j] = std::min(dis[i][j] ,dis[i][j + n]);
    			org.A[i][j] = dis[i][j];
    		}
    	}
    	
    	// tr.output();
    	while(K){
    		if(K & 1) org = org * tr;
    		tr = tr * tr ,K >>= 1;
    	}
    	printf("%lld
    " ,org.A[1][n]);
    
    	return 0;
    }
    
  • 相关阅读:
    HttpClient——POST请求方法
    【Httpclient+TestNG】接口自动化测试——GET请求示例
    一些杂碎的小知识点
    搭建linux服务器的环境
    javase(1)
    事务处理
    数据库优化
    表单重复提交--->使用Session防止表单重复提交
    两个小程序
    java基础小知识(2)
  • 原文地址:https://www.cnblogs.com/imakf/p/13848196.html
Copyright © 2011-2022 走看看