ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[NOI Online 2020 #1]魔法 题解

2021-07-29 21:01:17  阅读:134  来源: 互联网

标签:right NOI ch 题解 base 2020 ans left define


题意简述

给定一个 \(n\) 的点 \(m\) 条边的带权有向图,你可以进行至多 \(k\) 次操作,使得下一次通过路径的权值变为其相反数,之后再变回来。

问从 \(1\) 号点到 \(n\) 号点的最短路。

\(n≤100,m≤2500,k≤10^6\)。

Solution

先考虑 \(70\) 分怎么做:

\(k=0\) 时直接跑个 \(floyd\) 就好了。

\(k=1\) 时也很简单,枚举对哪条边使用魔法。

记 \(f(k,i,j)\) 为进行至多 \(k\) 次操作,从 \(i\) 到 \(j\) 的最短路,有方程 \(f(1,i,j)=min \left\{f(0,i,e_u)+f(0,e_v,j)-e_t\right\}\)。

拓展到 \(k\) 较大时的情况,有 \(f(k,i,j)=min \left\{f(x,i,p)+f(k-x,p,j)\right\} \left(0≤x≤k\right)\)。

这个转移方程的形式就非常得好,它是一个广义矩阵乘法,满足结合律,于是我们可以直接对其矩阵快速幂。

在 \(NOI2020\) 的 \(D1T1\) 也有用到这个东西,还是蛮重要的。

记得特判 \(k=0\)。

Code

#include<bits/stdc++.h>
#define IL inline
#define RE register
#define ll long long
#define N 110
#define M 2550
#define INF (1ll<<60)
IL int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+(ch^48),ch=getchar();
	return x*f;
}

int n,m,k;

template<class T1,class T2>IL void cmin(T1 &x,T2 y){x=x<y?x:y;}

struct Matrix
{
	ll a[N][N];
	Matrix(){memset(a,63,sizeof(a));}
	void copy(ll b[][N]){for(RE int i=1;i<=n;++i)for(RE int j=1;j<=n;++j)cmin(a[i][j],b[i][j]);}
	Matrix operator *(const Matrix &x)const
	{
		Matrix ret;
		for(RE int i=1;i<=n;++i)
			for(RE int j=1;j<=n;++j)
				for(RE int k=1;k<=n;++k)
					cmin(ret.a[i][j],a[i][k]+x.a[k][j]);
		return ret;
	}
}base,ans;

struct Edge{int u,v,t;}e[M];

ll dis[N][N];

IL void floyd()
{
	for(RE int k=1;k<=n;++k)
		for(RE int i=1;i<=n;++i)
			for(RE int j=1;j<=n;++j)
				cmin(dis[i][j],dis[i][k]+dis[k][j]);
}

int main()
{
	n=read(),m=read(),k=read();
	memset(dis,63,sizeof(dis));	
	for(RE int i=1;i<=n;++i)dis[i][i]=0;
	for(RE int i=1;i<=m;++i)
	{
		e[i]=(Edge){read(),read(),read()};
		dis[e[i].u][e[i].v]=e[i].t;
	}
	floyd();	
	if(!k)return printf("%lld",dis[1][n]),0;
	for(RE int i=1;i<=m;++i)
		for(RE int x=1;x<=n;++x)
			for(RE int y=1;y<=n;++y)
				cmin(base.a[x][y],dis[x][e[i].u]+dis[e[i].v][y]-e[i].t);
	base.copy(dis),ans.copy(dis);
	for(;k;k>>=1,base=base*base)if(k&1)ans=ans*base;
	printf("%lld",ans.a[1][n]);
	return 0;
}

标签:right,NOI,ch,题解,base,2020,ans,left,define
来源: https://www.cnblogs.com/Nesraychan/p/15077049.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有