Evanyou Blog 彩带

洛谷P3758/BZOJ4887 [TJOI2017] 可乐 [矩阵快速幂]

  洛谷传送门BZOJ传送门

可乐

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 299  Solved: 207

Description

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

Input

第一行输入两个正整数N,M表示城市个数,M表示道路个数。(1≤N≤30,0≤M≤100)
接下来M行输入u,v表示u,v之间有一条道路。
(1≤u,v≤n)保证两座城市之间只有一条路相连。
最后输入时间t。1<t≤10^6

Output

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

Sample Input

3 2
1 2
2 3
2

Sample Output

8

HINT

Source

 


  分析:

  一道人类智慧题,思路无比妙。

  大多数人第一眼看到这题反应应该都是$DP$,出题人貌似也没想卡一般的$DP$(用$DP$+矩阵加速也是可以以非常优秀的效率过掉的),因此写的好看的$DP$也可以过,不过可能会需要吸一口氧气。

  正解是矩阵快速幂。

  嗯???矩阵快速幂??这题和矩阵快速幂有关系??是的,正解就是矩阵快速幂。juruo一开始也没想到,还是看了一位julao的思路才豁然开朗。

  首先看,$n$的范围只有$30$,明显可以用邻接矩阵。而这题的突破口就在这里。我们来思考,如果对邻接矩阵$A$做快速幂会怎样?

  从$Floyd$算法的角度分析,没有边权时(即默认所有边的边权为$1$时),$A^k$中的任意一个元$a_{i,j}$表示从$i$到$j$经过$k$条边的方案数。这个不太方便字面上解释,可以自己根据矩阵乘法的法则结合具体例子分析一下。

  对于这题,因为可以留在原地,所以我们可以把所有点都加上一个自环。还有爆炸的情况,我们可以把爆炸当作第$0$号点,并单方面建立所有点到$0$号点的单向边,这样的过的话到达$0$号点以后就不会再到其他点,就能表示出爆炸的情况了。最后统计的答案应该就是从$1$号点到所有点经过$k$条边的情况总和,也就是$\sum ^n_{i=0}a[1][i]$。

  剩下的就是矩阵快速幂的模板了。

  不得不说,真是一道人类智慧题。

  Code:

  

//It is made by HolseLee on 6th Sep 2018
//Lougu.org P3758
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int mod=2017;
int n,m,t,ans;
struct Matrix {
    int a[31][31];
    Matrix() { memset(a,0,sizeof(a)); }
    Matrix(int b[31][31]) { memcpy(a,b,sizeof(a)); }
    friend Matrix operator * (const Matrix x,const Matrix y) {
        Matrix 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]=(ret.a[i][j]+(x.a[i][k]*y.a[k][j]))%mod;
        }
        return ret;
    } 
}H,L;

int main()
{
    scanf("%d%d",&n,&m);
    int x,y;
    for(int i=1; i<=m; ++i) {
        scanf("%d%d",&x,&y);
        H.a[x][y]=H.a[y][x]=1;
    }
    for(int i=0; i<=n; ++i) H.a[i][0]=1, H.a[i][i]=1;
    for(int i=0; i<=n; ++i) L.a[i][i]=1;
    scanf("%d",&t);
    while( t ) {
        if( t&1 ) L=L*H;
        t>>=1; H=H*H;
    }
    for(int i=0; i<=n; ++i)
    ans=(ans+L.a[1][i])%mod;
    printf("%d",ans);
    return 0;
}

 

posted @ 2018-09-06 19:33  HolseLee  阅读(324)  评论(0编辑  收藏  举报