洛谷5789 [TJOI2017]可乐(矩阵快速幂,Floyd思想)

题意:可乐机器人有三种行为: 停在原地,去下一个相邻的城市,自爆。它每一秒都会随机触发一种行为。现在给加里敦星球城市图,在第 0秒时可乐机器人在 1号城市,问经过了 t秒,可乐机器人的行为方案数是多少?(洛谷5789)

输入格式:第一行输入两个正整数 N,M, N表示城市个数,M表示道路个数。接下来 M 行输入 u,v,表示 u,v之间有一条双向道路。最后输入时间 t。

输出格式:输出可乐机器人的行为方案数,答案可能很大,请输出对 2017取模后的结果。

分析:设现在有一个邻接矩阵A那么Ak的意义是什么?(两个点之间若有边则A[u][v]=1)

从floyd算法的角度考虑,不难发现Ak的第i行第j列的数字含义是从i到j经过k步的路径方案总数。

在原地停留很简单,我们只要认为每个点都有一个从自己到自己的自环即可。那自爆呢?我们可以将自爆这个状态也看成一个城市,就设它为编号0。我们在邻接矩阵上从每个点都向这个点连一条边,这个点除了自己外不连其他出边。这样就满足了任何一个点随时可以自爆,且无法恢复到其他状态。最后统计答案ans=$sum_{i=0}^{n}$A[1][i]

#include<cstdio>
#include<cstring>
#define min(a,b) (a)<(b) ? (a):(b)

int n,t,s,e,tot,u,v,w;
int num[1005];

struct Node{
    int dis[205][205];
    Node operator *(const Node &x)const{
        Node ans;
        memset(ans.dis,0x3f,sizeof(ans.dis));
        for(int i = 1; i <= tot; ++i)
            for(int t = 1; t <= tot; ++t)
                for(int k = 1; k <= tot; ++k)
                    ans.dis[i][t] = min(ans.dis[i][t],dis[i][k]+x.dis[k][t]);
        return ans;
    }
}a,b;

void quick_pow(int n){
    b = a;
    while(n){
        if(n&1)  b = a*b;
        a = a*a;  n >>= 1;
    }
}

int main(){
    scanf("%d%d%d%d",&n,&t,&s,&e);
    memset(a.dis,0x3f,sizeof(a.dis));
    for(int i = 1; i <= t; ++i){
        scanf("%d%d%d",&w,&u,&v);
        if(!num[u])  num[u] = ++tot;
        if(!num[v])  num[v] = ++tot;
        a.dis[num[u]][num[v]] = a.dis[num[v]][num[u]] = w;
    }
    quick_pow(n-1);
    printf("%d",b.dis[num[s]][num[e]]);
    return 0;
}
你只有十分努力,才能看上去毫不费力。
原文地址:https://www.cnblogs.com/214txdy/p/14024005.html