您的位置:首页 > 其它

[BZOJ 1297][SCOI2009]迷路

2017-11-07 10:09 423 查看

1297: [SCOI2009]迷路

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1418 Solved: 1017
[Submit][Status][Discuss]

Description

windy在有向图中迷路了。 该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1。 现在给出该有向图,你能告诉windy总共有多少种不同的路径吗? 注意:windy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

Input

第一行包含两个整数,N T。 接下来有 N 行,每行一个长度为 N 的字符串。 第i行第j列为'0'表示从节点i到节点j没有边。 为'1'到'9'表示从节点i到节点j需要耗费的时间。

Output

包含一个整数,可能的路径数,这个数可能很大,只需输出这个数除以2009的余数。

Sample Input

【输入样例一】

2 2

11

00

【输入样例二】

5 30

12045

07105

47805

12024

12345

Sample Output

【输出样例一】

1

【样例解释一】

0->0->1

【输出样例二】

852

HINT

30%的数据,满足 2 <= N <= 5 ; 1 <= T <= 30 。 100%的数据,满足 2 <= N <= 10 ; 1 <= T <= 1000000000 。


一周之前做的了...想了想还是写写题解吧...

题解

首先我们都知道的一个结论是对于一个简单图 $G$ 的邻接矩阵 $M$ , $M^k_{i,j}$ 就是从点 $i$ 走 $k$ 条边到点 $j$ 的方案数.

然而这个结论只适用于两点间的边数, 而我们注意到本题中两点间的边是带权的, 我们就不能直接使用这个结论了

接着我们可以想到的其中一个解法是把一条边权为 $k$ 的边通过在其中插入虚拟结点的方式拆成 $k$ 条边, 但是极端情况下可能有 $100$ 条带权边要拆, 每条带权边可能会拆成 $10$ 条无权边, 其中会产生大量虚拟结点, 极端情况下总结点数会大于 $1000$ , 矩阵相应的也会变成这个数量级, 然后一次 $O(n^3)$ 的矩阵乘法都跑不完...╮(╯﹏╰)╭

继续思考优化方式, 现在的瓶颈在于虚拟结点过多, 我们可以思考如何缩减虚拟结点的数量. 我们可以尝试事先将一些虚拟结点和原结点连成链, 然后对于带权边都指向这条链上的对应位置, 将带权边前方的链合并起来(一股Trie的味道?)来最小化结点数量, 这样就可以把结点数量控制在 $100$ 的量级

然后就很棒棒了, 构造完矩阵无脑跑快速幂就好了(o゚▽゚)o

反正别像我一样打出了正解输出的时候把倍增矩阵当成答案矩阵输出就行了( ̄. ̄)

参考代码

GitHub

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>

const int MAXN=110;
const int MOD=2009;

int n,t;
char buf[MAXN];
int m[MAXN][MAXN];
int x[MAXN][MAXN];
int mt[MAXN][MAXN];

int Encode(int);
void Initialize();
int Encode(int,int);

int main(){
Initialize();
// t--;
while(t>0){
if((t&1)!=0){
memset(mt,0,sizeof(mt));
for(int i=0;i<n*9;i++)
for(int j=0;j<n*9;j++)
for(int k=0;k<n*9;k++)
(mt[i][j]+=x[i][k]*m[k][j])%=MOD;
memcpy(x,mt,sizeof(mt));
}
memset(mt,0,sizeof(mt));
for(int i=0;i<n*9;i++)
for(int j=0;j<n*9;j++)
for(int k=0;k<n*9;k++)
(mt[i][j]+=m[i][k]*m[k][j])%=MOD;
memcpy(m,mt,sizeof(mt));
t>>=1;
}
printf("%d\n",x[0][Encode(n-1)]);
return 0;
}

void Initialize(){
scanf("%d%d",&n,&t);
for(int i=0;i<n;i++){
for(int j=2;j<=9;j++){
m[Encode(i,j)][Encode(i,j-1)]=1;
}
}
for(int i=0;i<n;i++){
scanf("%s",buf);
for(int j=0;j<n;j++){
if(buf[j]!='0'){
m[Encode(i)][Encode(j,buf[j]-'0')]=1;
}
}
}
for(int i=0;i<n*9;i++)
x[i][i]=1;
}

inline int Encode(int k,int len){
return k*9+len-1;
}

inline int Encode(int k){
return k*9;
}


Backup

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: