洛谷P3758 [TJOI2017]可乐

12 篇文章 0 订阅
11 篇文章 0 订阅

题目描述

加里敦星球的人们特别喜欢喝可乐。因而,他们的敌对星球研发出了一个可乐机器人,并且放在了加里敦星球的1号城市上。这个可乐机器人有三种行为: 停在原地,去下一个相邻的城市,自爆。它每一秒都会随机触发一种行为。现 在给加里敦星球城市图,在第0秒时可乐机器人在1号城市,问经过了t秒,可乐机器人的行为方案数是多少?

输入输出格式

输入格式:

 

第一行输入两个正整数况N,M,N表示城市个数,M表示道路个数。(1 <= N <=30,0 < M < 100)

接下来M行输入u,v,表示u,v之间有一条道路。(1<=u,v <= n)保证两座城市之间只有一条路相连。

最后输入入时间t

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 复制

3 2
1 2
2 3
2

输出样例#1: 复制

8

说明

【样例解释】

1 ->爆炸

1 -> 1 ->爆炸

1 -> 2 ->爆炸

1 -> 1 -> 1

1 -> 1 -> 2

1 -> 2 -> 1

1 -> 2 -> 2

1 -> 2 -> 3

【数据范围】

对于20%的pn,有1 < t ≤ 1000

对于100%的pn,有1 < t ≤ 10^6。

 

题解:

还是膜拜大神吧。

 

这道题我们可以从邻接矩阵的意义考虑。

设现在有一个邻接矩阵 AA 。

那么 A^kAk 的意义是什么?(两个点之间若有边则 A[u][v]=1A[u][v]=1 )

从 floydfloyd 算法的角度考虑,不难发现 A^kAk 的第 ii 行第 jj 列的数字含义是从 ii 到 jj 经过 kk 步的路径方案总数。

从这个角度考虑,这个点就有了一种做法。

首先将这个图的邻接矩阵建出来,然后直接算这个矩阵的 kk 次方。

最后统计 \sum\limits_{i=1}^{n}A[1][i]i=1∑n​A[1][i] 就是答案。

那么在原地停留和自爆怎么处理?

在原地停留很简单,我们只要认为每个点都有一个从自己到自己的自环即可。

那自爆呢?

我们可以将自爆这个状态也看成一个城市,就设它为编号 00 。

我们在邻接矩阵上从每个点都向这个点连一条边,这个点除了自己外不连其他出边。

这样就满足了任何一个点随时可以自爆,且无法恢复到其他状态。

最后,统计答案。 Ans=\sum\limits_{i=0}^{n}A[1][i]Ans=i=0∑n​A[1][i]

 

代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int P=2017;
struct Matrix{
    int a[31][31];
    inline Matrix operator * (const Matrix &rhs)
    {
        Matrix ret;
        memset(&ret,0,sizeof ret);
        for(int i=0;i<=30;i++)
            for(int j=0;j<=30;j++)
                for(int k=0;k<=30;k++)
                    (ret.a[i][j]+=a[i][k]*rhs.a[k][j]%P)%=P;
        return ret;
    }
}mp;
Matrix ksm(Matrix &a,int b)
{
    Matrix ret;
    memset(&ret,0,sizeof ret);
    for(int i=0;i<=30;i++) ret.a[i][i]=1;
    while(b)
    {
        if(b&1) ret=ret*a;
        a=a*a;b>>=1;
    }
    return ret;
}
int u,v,n,m,t;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        mp.a[u][v]=1;mp.a[v][u]=1;
    }
    for(int i=0;i<=n;i++)
        mp.a[i][i]=1;
    for(int i=1;i<=n;i++) mp.a[i][0]=1;
    int ans=0;
    scanf("%d",&t);
    Matrix anss=ksm(mp,t);
    for(int i=0;i<=n;i++) (ans+=anss.a[1][i])%=P;
    printf("%d\n",ans);
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值