题干:
FJ的N(2<=N<=1,000,000)头奶牛选择了接力跑作为她们的日常锻炼项目。至于进行接力跑的地点 自然是在牧场中现有的T(2 <= T <= 100)条跑道上。农场上的跑道有一些交汇点,每条跑道都连结了两个不同的交汇点 I1_i和I2_i(1<=I1_i<=1,000; 1<=I2_i<=1,000)。每个交汇点都是至少两条跑道的端点。 奶牛们知道每条跑道的长度length_i(1<=length_i<=1,000),以及每条跑道连结的交汇点的编号 并且,没有哪两个交汇点由两条不同的跑道直接相连。你可以认为这些交汇点和跑道构成了一张图。 为了完成一场接力跑,所有N头奶牛在跑步开始之前都要站在某个交汇点上(有些交汇点上可能站着不只1头奶牛)。当然,她们的站位要保证她们能够将接力棒顺次传递,并且最后持棒的奶牛要停在预设的终点。 你的任务是,写一个程序,计算在接力跑的起点(S)和终点(E)确定的情况下,奶牛们跑步路径可能的最小总长度。显然,这条路径必须恰好经过N条跑道。
题解:
看到 n 这么大吓了一跳,最后看到 n 其实就是路径长度,基本上这就是矩阵快速幂了。但是节点大小为1~1000,显然难以承受,但是它的边不过 100 条,所以它有用的点最多为 101 个(连通),通过离散化可以实现(或者先建边,dfs一遍标个号即可)。将路径长度作为指数,直接将每个边的长度作为矩阵的值进行矩阵快速幂。
为什么要将将每个边的长度直接作为矩阵的值?不用缩点吗?若我们进行缩点,100000 个点一定无法接受。再者,其实若是求必经 n 条路的最短路径长度,我们矩阵的定义就变为了由 x 到 y 节点必须经过 z 条路径的最小值,而每个节点的值就是路径长度,所以不需缩点。
但是如果是普通的矩阵快速幂,它求出的只是经过一个点的路径种类数,无法满足我们的预期,我们可以将矩阵乘法由题意改变一下,变为:
sum.a[i][j]=min(sum.a[i][j],aa.a[i][k]+bb.a[k][j]);
可以发现,把矩阵乘法的 ∑ 改成取 min 一样满足分配律;而且矩阵乘几次,它每个点所表示的就变为了恰好经过 n 的长度(可以走重边)到达这个点(坐标)的方案总数。(本题就是最短距离)所以我们是可以将矩阵乘法变为我们所需要的形式。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #define $ 111
5 using namespace std;
6 int m,n,t,start,done,in[$],sta[$*2],out[$],w[$],up;
7 inline int min(int x,int y){ return x<y?x:y; }
8 struct tree{
9 int a[$][$];
10 tree(){ memset(a,63,sizeof(a)); }
11 friend tree operator * (tree aa,tree bb){
12 tree sum=tree();
13 for(register int k=1;k<=m;++k)
14 for(register int i=1;i<=m;++i)
15 for(register int j=1;j<=m;++j)
16 sum.a[i][j]=min(sum.a[i][j],aa.a[i][k]+bb.a[k][j]);
17 return sum;
18 }
19 friend tree operator ^ (tree aa,int x){
20 tree sum=tree();
21 for(register int i=1;i<=m;++i) sum.a[i][i]=0;
22 for(;x;x>>=1,aa=aa*aa) if(x&1) sum=sum*aa;
23 return sum;
24 }
25 };
26 signed main(){
27 tree ans=tree();
28 scanf("%d%d%d%d",&n,&t,&start,&done);
29 for(register int i=1;i<=t;++i)
30 scanf("%d%d%d",&w[i],&in[i],&out[i]),sta[++up]=in[i],sta[++up]=out[i];
31 sta[++up]=start, sta[++up]=done;
32 sort(sta+1,sta+up+1);
33 m=unique(sta+1,sta+up+1)-sta-1;
34 for(register int i=1;i<=t;++i){
35 in[i]=lower_bound(sta+1,sta+m+1,in[i])-sta;
36 out[i]=lower_bound(sta+1,sta+m+1,out[i])-sta;
37 ans.a[in[i]][out[i]]=ans.a[out[i]][in[i]]=w[i];
38 }
39 start=lower_bound(sta+1,sta+m+1,start)-sta;
40 done= lower_bound(sta+1,sta+m+1,done)-sta;
41 ans=ans^n;
42 printf("%d
",ans.a[start][done]);
43 }