头像

欢迎来到我的博客

分享题解与总结

P2886 Cow Relays G

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
posted @ 2026-08-27 20:31  king_steph1209  阅读(3)  评论(0)    收藏  举报