zoukankan      html  css  js  c++  java
  • 【BZOJ-4386】Wycieczki DP + 矩阵乘法

    4386: [POI2015]Wycieczki

    Time Limit: 20 Sec  Memory Limit: 128 MB
    Submit: 197  Solved: 49
    [Submit][Status][Discuss]

    Description

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

    Input

    第一行包含三个整数n,m,k(1<=n<=40,1<=m<=1000,1<=k<=10^18)。
    接下来m行,每行三个整数u,v,c(1<=u,v<=n,u不等于v,1<=c<=3),表示从u出发有一条到v的单向边,边长为c。
    可能有重边。

    Output

    包含一行一个正整数,即第k短的路径的长度,如果不存在,输出-1。

    Sample Input

    6 6 11
    1 2 1
    2 3 2
    3 4 2
    4 5 1
    5 3 1
    4 6 3

    Sample Output

    4

    HINT

    长度为1的路径有1->2,5->3,4->5。
    长度为2的路径有2->3,3->4,4->5->3。
    长度为3的路径有4->6,1->2->3,3->4->5,5->3->4。
    长度为4的路径有5->3->4->5。

    Source

    鸣谢Claris

    Solution

    边权只有1,2,3三种,可以考虑拆点,那么只有边权为1的边了

    那么显然可以DP,但是时间复杂度不允许

    考虑用矩阵乘法去转移

    这里比较优秀的方法是基于倍增的矩阵乘法

    总复杂度是$O(n^{3}logK)$

    答案会很大,乘爆longlong需要特判

    这题细节非常多!

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    inline long long read()
    {
        long long 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-'0'; ch=getchar();}
        return x*f;
    }
    #define MAXN 130
    int id[MAXN][4],ID,N,M,B;
    long long K,V[MAXN*3];
    struct MatrixNode{long long a[MAXN][MAXN];}a[100],b,c,tmp;
    MatrixNode operator * (const MatrixNode &A,const MatrixNode &B)
    {
        MatrixNode C; memset(C.a,0,sizeof(C.a));
            for (int i=0; i<=ID; i++)
                for (int j=0; j<=ID; j++)
                    for (int k=0; k<=ID; k++)
                        if (A.a[i][k]&&B.a[k][j])
                            {
                                if (A.a[i][k]<0 || B.a[k][j]<0)  {C.a[i][j]=-1; break;}
                                if (A.a[i][k]>K/B.a[k][j]) {C.a[i][j]=-1; break;}
                                C.a[i][j]+=A.a[i][k]*B.a[k][j];
                                if (C.a[i][j]>K) {C.a[i][j]=-1; break;}
                            }    
        return C;        
    }
    bool Check()
    {
        long long re=0;
        for (int i=0; i<=ID; i++)
            if (tmp.a[0][i] && V[i])
                {
                    if (tmp.a[0][i]<0) return 0;
                    if (tmp.a[0][i]>K/V[i]) return 0;
                    re+=tmp.a[0][i]*V[i];
                    if (re>K) return 0;
                }
        return re<K;
    }
    int main()
    {
        N=read(),M=read(),K=read();
        for (int i=1; i<=N; i++) for (int j=0; j<=2; j++) id[i][j]=++ID;
        for (int i=1; i<=N; i++)
            {
                for (int j=0; j<=1; j++)
                    a[0].a[id[i][j]][id[i][j+1]]++;
                a[0].a[0][id[i][0]]++;
            }
        a[0].a[0][0]++;
        for (int x,y,z,i=1; i<=M; i++) x=read(),y=read(),z=read(),a[0].a[id[y][z-1]][id[x][0]]++,V[id[y][z-1]]++;
        for (int i=0; (1LL<<i)<=K*3; B=++i); B--;
        for (int i=1; i<=B; i++) a[i]=a[i-1]*a[i-1];
        long long ans=0;
        for (int i=0; i<=ID; i++) c.a[i][i]=1;
        for (int i=B; ~i; i--)
            {
                tmp=c*a[i];
                if (Check()) ans|=(1LL<<i),memcpy(c.a,tmp.a,sizeof(tmp.a));
            }
        ans++;
        if (ans>K*3) {puts("-1"); return 0;}
            else printf("%lld
    ",ans);
        return 0;
    }

    模拟赛的时候,没认真读题,以为是“魔法猪学院”类似的...等到半场才发现.....然后GG

  • 相关阅读:
    二分图的部分关系
    二分图的部分关系
    日常训练赛 Problem C – Complete Naebbirac’s sequence
    日常训练赛 Problem C – Complete Naebbirac’s sequence
    J
    J
    Python strip()方法
    Python startswith()方法
    Python splitlines()方法
    Python split()方法
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5759542.html
Copyright © 2011-2022 走看看