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;
}
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧