P2886 Cow Relays G
P2886 Cow Relays G
牛径
题目:
"路径长度" → \(+\) , 最小 → \(min\)
从而快速幂中的矩阵乘法 → 矩阵加法
\(A_{i,j}^1\):定义为\(i → j\)只经过 1 条边的 \(min\{路径长度\}\)
\(A_{i,j}^n\):定义为\(i → j\)经过 n 条边的 \(min\{路径长度\}\)
C.a[i][j]=min(C.a[i][j],A.a[i][k]+B.a[k][j])
这里考虑矩阵加法的含义,根据:
其中每一次计算\(A_{i,k} \times A_{k,j}\),都是相当于是:\(i \rightarrow k \rightarrow j\)是一种方案,统计该方案对\(C_{i,j}\)的贡献
得出:我们每次计算\(A_{i,k} + A_{k,j}\),都相当于\(i \rightarrow k \rightarrow j\)是一种方案,看看这种方案能不能对答案有贡献
这里相当于一种松弛操作
E.G.
一共5个点
初始时,\(A_{2,3}^1=2,A_{3,5}^1=5,A_{2,5}^1=12\)
\[A_{2,5}^1=min\{ A_{2,5}^1,A_{2,1}^1+A_{1,5}^1 \\
A_{2,5}^1,A_{2,2}^1+A_{2,5}^1 \\
A_{2,5}^1,A_{2,3}^1+A_{3,5}^1 \\
A_{2,5}^1,A_{2,4}^1+A_{4,5}^1 \\
A_{2,5}^1,A_{2,5}^1+A_{5,5}^1
\}
\]
其中,
换行:行内两两取\(min\)
\(A_{2,5}^1=min\{A_{2,5}^1,A_{2,3}^1+A_{3,5}^1\}\)就是从\(2 → 3 → 5\)这种方案,经过的路径长度(or 权值之和)为7,看看能不能缩小\(2 → 5\)的权值和
另外,需要将 \(T\) 条牛径的端点离散化
代码:
const int inf=INT_MAX;
struct Matrix{
vector<vector<ll>> a;
Matrix(int n){
a.resize(n);
for(int i=0;i<n;++i) a[i].resize(n,inf);
}
};
Matrix operator*(const Matrix& A,const Matrix& B)
{
int n=A.a.size();
Matrix C(n);
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
for(int k=0;k<n;++k){
C.a[i][j]=min(C.a[i][j],A.a[i][k]+B.a[k][j]);
}
}
}
return C;
}
struct Vec{
vector<ll> a;
Vec(int n){
a.resize(n,inf);
}
};
Vec operator*(const Matrix& A,const Vec& B){
int n=A.a.size();
Vec C(n);
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
C.a[i]=min(C.a[i],A.a[i][j]+B.a[j]);
}
}
return C;
}
Matrix qpow(Matrix A,ll k)
{
int n=A.a.size();
Matrix ans(n);
for(int i=0;i<n;++i) ans.a[i][i]=0;//!!!
while(k){
if(k&1) ans=ans*A;
A=A*A;
k>>=1;
}
return ans;
}
int main()
{
int n,t,s,e;
cin>>n>>t>>s>>e;
Matrix A(2*t);
vector<tuple<int,int,int>> arr;
vector<int> a0,a1;
for(int i=0;i<t;++i){
int w,u,v;
cin>>w>>u>>v;
arr.push_back({w,u,v});
a0.push_back(u);
a0.push_back(v);
}
sort(a0.begin(),a0.end());
a0.erase(unique(a0.begin(),a0.end()),a0.end());
for(auto& [w,u,v]:arr){
int u2=lower_bound(a0.begin(),a0.end(),u)-a0.begin();
int v2=lower_bound(a0.begin(),a0.end(),v)-a0.begin();
A.a[u2][v2]=w;
A.a[v2][u2]=w;
}
A=qpow(A,n);
int s2=lower_bound(a0.begin(),a0.end(),s)-a0.begin();
int e2=lower_bound(a0.begin(),a0.end(),e)-a0.begin();
cout<<A.a[s2][e2];
return 0;
}
另外:
\((\oplus,\otimes)=(|,\&)\)
这种运算的广义矩阵乘法均满足结合律
|和&的结合律证明省略
&对|的左/右分配律:可固定1位,穷举8种0/1,验证\((a|b)&c = (a&c)|(b&c)\)
\(a_i\):a的第i位
| \(a_i\) | \(b_i\) | \(c_i\) | (a_i | b_i) & c_i | (a_i & c_i) | (b_i & c_i) |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 | 0 |
| 0 | 1 | 0 | 0 | 0 |
| 0 | 1 | 1 | 1 | 1 |
| 1 | 0 | 0 | 0 | 0 |
| 1 | 0 | 1 | 1 | 1 |
| 1 | 1 | 0 | 0 | 0 |
| 1 | 1 | 1 | 1 | 1 |

浙公网安备 33010602011771号