POI2015 WYC

也许更好的阅读体验

\(\mathcal{Description}\)
给定一张n个点m条边的带权有向图,每条边的边权只可能是1,2,3中的一种。将所有可能的路径按路径长度排序,请输出第k小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点。
\((1\leq n\leq 40,1\leq m\leq 1000,1\leq k\leq 10^{18})\)

\(\mathcal{Solution}\)
死毒瘤题,打了一晚上,最后把自己的方法改的和其他人差不多了
边权不为\(1\),不好直接套用邻接矩阵,考虑把一个点拆开,让一条长度大于\(1\)的路径要到一些没用的点使得它需要走多次才能走到它该到的点
把一个点\(n\)拆为\(n_0,n_1,n_2\),分别表示距离\(n\)还有\(0/1/2\)的距离,实际上,\(n_0\)就是原本的点,我们称之为实点,\(n_1,n_2\)则是用来消耗路径长度的点,称之为虚点
先有\(n_2\ \rightarrow\ n_1,n_1\ \rightarrow \ n_0\)连边
对一条边\(\left(u,v,w\right)\),考虑由实点\(u_0\)走出去一步,那么距离\(v\)就会减一,所以\(u_0\)\(v_{w-1}\)连一条边,即连边\(\left(u_0,v_{w-1}\right)\)

现在我们可以对现在的矩阵做乘法和快速幂了,每个实点到实点的权值就是方案数
如现在是这个矩阵的\(k\)次方,则矩阵上\(\left(u_0,v_0\right)\)上的权值表示的就是从\(u_0\)出发,走\(k\)步走到\(v_0\)的方案数

接下来考虑怎么求答案,\(k\)很大,自然地就想到了倍增,类似求\(LCA\)一样的去确定答案即可

当然,直接对矩阵求\(p\)次方,那么求出来的矩阵里的值表示的是刚好走\(p\)步从某个位置走到某个位置的答案
为了方便的进行判断,我们需要把矩阵的值表示成走小于等于\(p\)步的方案数
所以要考虑把走过的答案存下来,我们可以用\(0\)号点表示所有的方案数
怎么用\(0\)号点表示呢,考虑每次算出一个值后再算下一个值时将当前的答案放到\(0\)号点去
所以对每个点实点\(u_0\)连一条\(\left(u_0,0\right)\)的边即可,而每次计算不能把上次的答案丢了,所以还要连一条\(\left(0,0\right)\)的边

这样当前矩阵的\(0\)号点就表示着上一次的答案
我们再弄一个矩阵乘上当前矩阵就可以表示,这个矩阵\(0\)向所有的实点连一条边即可

\(\mathcal{Code}\)

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年11月06日 星期三 18时58分03秒
*******************************/
#include <cstdio>
#include <fstream>
#define ll long long
#define ld long double
#define rint register int
using namespace std;
const int maxn = 130;
//{{{cin
struct IO{
	template<typename T>
	IO & operator>>(T&res){
		res=0;
		bool flag=false;
		char ch;
		while((ch=getchar())>'9'||ch<'0')	flag|=ch=='-';
		while(ch>='0'&&ch<='9')	res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
		if (flag)	res=~res+1;
		return *this;
	}
}cin;
//}}}
int n,lim,m;
ll k,ans;
//{{{Matrix
struct Matrix{
	ld mat[maxn][maxn];
	Matrix (bool opt=0){
		for (int i=0;i<=lim;++i)
			for (int j=0;j<=lim;++j)	mat[i][j]=opt&&(i==j);
	}
	ld* operator [] (const int &x){	return mat[x];}
	Matrix operator * (Matrix &b){
		Matrix a=*this,s;
		for (int i=0;i<=lim;++i)
			for (int j=0;j<=lim;++j)
				for (int k=0;k<=lim;++k)
					s[i][j]+=a[i][k]*b[k][j];
		return s;
	}
}g[maxn],s;
//}}}
inline int loc (int x,int i){	return (x-1)*3+i+1;}
inline ld sum (Matrix &a) { return a[0][0]-n; }
int main()
{
	freopen("p3597.in","r",stdin);
	freopen("p3597.out","w",stdout);
	cin>>n>>m>>k;
	lim=3*n;

	for (rint i=1;i<=n;++i){
		for (rint j=1;j<=2;++j)	g[0][loc(i,j)][loc(i,j-1)]=1;
		g[0][loc(i,0)][0]=s[0][loc(i,0)]=1;
	}
	g[0][0][0]=1;

	for (rint i=1;i<=m;++i){
		int u,v,d;
		cin>>u>>v>>d;
		++g[0][loc(u,0)][loc(v,d-1)];
	}


	int d;
	for (d=1;;++d){
		g[d]=g[d-1]*g[d-1];
		Matrix t=s*g[d];
		if (sum(t)>=k)	break;
		if (d>=64)	return printf("-1\n"),0;
	}

	for (rint i=d;~i;--i){
		Matrix t=s*g[i];
		if (sum(t)<k){
			s=t;
			ans+=(1ll<<i);
		}
	}

	printf("%lld\n",ans);
	return 0;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

posted @ 2019-11-07 19:23  Morning_Glory  阅读(247)  评论(0编辑  收藏  举报
//