• POI2015 WYC


    也许更好的阅读体验

    (mathcal{Description})
    给定一张n个点m条边的带权有向图,每条边的边权只可能是1,2,3中的一种。将所有可能的路径按路径长度排序,请输出第k小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点。
    ((1leq nleq 40,1leq mleq 1000,1leq kleq 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 ightarrow n_1,n_1 ightarrow n_0)连边
    对一条边(left(u,v,w ight)),考虑由实点(u_0)走出去一步,那么距离(v)就会减一,所以(u_0)(v_{w-1})连一条边,即连边(left(u_0,v_{w-1} ight))

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

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

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

    这样当前矩阵的(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
    "),0;
    	}
    
    	for (rint i=d;~i;--i){
    		Matrix t=s*g[i];
    		if (sum(t)<k){
    			s=t;
    			ans+=(1ll<<i);
    		}
    	}
    
    	printf("%lld
    ",ans);
    	return 0;
    }
    

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

  • 相关阅读:
    【转】 mysql反引号的使用(防冲突)
    【百度一键分享功能】百度一键分享插件
    【WEB2.0】 网页调用QQ聊天(PC+M站)
    python : takes 0 positional arguments but 1 was given
    python : 设计模式之外观模式(Facade Pattern)
    Tosca : 扩展dll动态语言 识别点击下拉, 识别成table
    Tosca : 把 inner text 放到变量里,定义变量,使用变量
    Tosca:键盘输入字符串
    Tosca 给定义变量,取内容放到变量里
    Tosca :配置环境参数
  • 原文地址:https://www.cnblogs.com/Morning-Glory/p/11814409.html
Copyright © 2020-2023  润新知