首页 > 其他 > 详细

POI2015 WYC

时间:2019-11-07 19:39:42      阅读:89      评论:0      收藏:0      [点我收藏+]

也许更好的阅读体验

\(\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;
}

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

POI2015 WYC

原文:https://www.cnblogs.com/Morning-Glory/p/11814409.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!