bzoj1706[usaco2007 Nov]relays 奶牛接力跑*

bzoj1706[usaco2007 Nov]relays 奶牛接力跑

题意:

无向图,求刚好经过n条边的最小距离。边数≤100,n≤1000000。

题解:

边数≤100,说明点数不超过200。故可以用floyd。但要用一点技巧,即倍增floyd:定义最短路矩阵之间的乘法为对它们做floyd,则最后答案矩阵为初始边矩阵的“n次幂”,同时这种乘法满足结合律,故可以用快速幂使得复杂度降低。本弱遇到了莫名其妙的问题,快速幂一写成递归形式就爆栈,样例都过不去,写成二进制拆分形式才能不RE。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <map>
 5 #define inc(i,j,k) for(int i=j;i<=k;i++)
 6 #define maxn 210
 7 #define ll long long
 8 #define INF 10000000000000000
 9 using namespace std;
10 
11 inline int read(){
12     char ch=getchar(); int f=1,x=0;
13     while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
14     while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
15     return f*x;
16 }
17 int tot,n,t,s,e; map<int,int>mp;
18 struct mat{
19     ll dis[maxn][maxn];
20 }f[25],ans;
21 mat operator * (mat a,mat b){
22     mat c; inc(i,1,n)inc(j,1,n)c.dis[i][j]=INF;
23     inc(k,1,n)
24         inc(i,1,n)
25             inc(j,1,n)c.dis[i][j]=min(c.dis[i][j],a.dis[i][k]+b.dis[k][j]);
26     return c;
27 }
28 int getnum(int x){
29     int y=mp[x]; if(!y){mp[x]=++n; return n;}else return y;
30 }
31 int main(){
32     tot=read(); t=read(); s=getnum(read()); e=getnum(read());
33     inc(i,1,t){
34         int x=read(),y=getnum(read()),z=getnum(read()); f[0].dis[y][z]=x; f[0].dis[z][y]=x;
35     }
36     inc(i,1,n)inc(j,1,n)if(!f[0].dis[i][j])f[0].dis[i][j]=INF;
37     for(int i=1;(1<<i)<=tot;i++)f[i]=f[i-1]*f[i-1]; bool fl=0;
38     for(int i=0;(1<<i)<=tot;i++)if(tot&(1<<i)){if(!fl)ans=f[i],fl=1;else ans=ans*f[i];}
39     printf("%lld",ans.dis[s][e]); return 0;
40 }

20160929

原文地址:https://www.cnblogs.com/YuanZiming/p/5971321.html