LGP7916 [CSP-S 2021] 交通规划 学习笔记

LGP7916 [CSP-S 2021] 交通规划 学习笔记

Luogu Link

前言

仔细读了十遍题面,硬是一个字都没和交通规划扯上关系,很有可能是出题人编了一个故事,发现编不下去了。——\(\texttt{OMG-WC}\)

题意简述

有一个 \(n\times m\) 个点的网格图。对于这个网格图的最外侧,有些网格线会延伸一格到一个附加点上。所有边均有非负整数边权。

给定每个附加点的颜色(橙或蓝),你可以随意给网格内那 \(n\times m\) 个点染色,染色方案的代价为所有两端点异色的边的边权和。问最小的代价。

\(2\le n,m\le 500\)\(T\le 50\)\(k_i\) 均在合法范围内。

特别地,有 \(45\text{pts}\) 部分分满足 \(k_i\le 2\)

做法解析

不妨从 \(k_i\le 2\) 的部分分考虑。显然,如果附加点颜色全部一致,那答案就是 \(0\)。那么,当 \(k_i=2\) 且两个附加点异色时,答案是一个最小割。参考 \(\texttt{LGP4001}\)\(\texttt{LGP2604}\) 等。

那么 \(k_i>2\) 的时候呢?此时的答案形态……。

我们发现,如果两个相邻的附加点颜色一致,那它们之间肯定没有隔阂(就是必然处于一个同色连通块中)。

pVf8Abj.png

所以我们不妨把相同颜色的点“缩在一起”,然后再次从转化为对偶图最短路的视角来考虑。我们发现此时最小割之和在对偶图上表现为:若干个源汇点(被分为两种颜色,相邻则异色)需要和异色点完成两两的括号匹配,每一对匹配的代价为我们在对偶图上跑出来的最短路,这样的代价和。

pVf8VVs.png

你可能会问:括号匹配肯定是偶数个点做,为什么一定有偶数个对偶图上的源汇点呢?答:因为对于原图来说最外侧同色的连续段肯定为偶数。连续段如果是奇数等价于必有两段相邻且同色,那么它们应该被缩成一段。当然,\(1\) 也是奇数,这个你直接特判就行了。

所以原理就是这样。你求出这些对偶图上异色源汇点两两的最短路之后,跑一个 \(O(k^3)\) 的括号匹配 \(\texttt{DP}\) 即可。

总复杂度:\(O(kmn\log mn+k^3)\)

代码实现

代码中,完整对偶图(假设所有射线都伸出来之后的对偶图)的最左上角的结点坐标被视为 \((0,0)\),编号为 \(1\)

哦还有很多细节。详见注释版代码,若可能。

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxK=60,MaxN=5e2+5,MaxV=3e5+5;
const lolo Inf=1e18;
int N,M,T,X,Y,cu,cv,K,cc,V;
vector<pli> Gr1[MaxV],Gr2[MaxV];
void addudge1(int u,int v,int w){
    Gr1[u].push_back({v,w});
    Gr1[v].push_back({u,w});
}
pii aget(int f){
    int u,v;
    if(f<=M)u=f,v=u+1;
    if(M<f&&f<=M+N)u=(f-M)*(M+1),v=u+(M+1);
    if(M+N<f&&f<=M*2+N)u=(N+2)*(M+1)+N-f,v=u-1;
    if(M*2+N<f)u=1+(N-(f-2*M-N)+1)*(M+1),v=u-(M+1);
    return {u,v};
}
void addudge2(int f,int w){
    auto [u,v]=aget(f);
    Gr2[u].push_back({v,w});
    Gr2[v].push_back({u,w});
}
pii S[MaxK];int scnt,sta[MaxK];
int vis[MaxN<<2];lolo dis[MaxV],wcon[MaxK][MaxK],dp[MaxK][MaxK];
priority_queue<pli,vector<pli>,greater<pli> > pq;
int trans(int x,int y){
    return x*(M+1)+y+1;
}
void dijkstra(int s){
    fill(dis+1,dis+V+1,Inf);
    dis[s]=0,pq.push({0,s});
    while(pq.size()){
        auto [di,u]=pq.top();pq.pop();
        if(di>dis[u])continue;
        auto loose=[&](vector<pli> &cgr){
            for(auto [v,w] : cgr){
                if(dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;
                    pq.push({dis[v],v});
                }
            }
        };
        loose(Gr1[u]),loose(Gr2[u]);
    }
}
struct oper{int w,f,c;}C[MaxK];
bool cmpf(oper a,oper b){return a.f<b.f;}
lolo solve(){
    sort(C+1,C+K+1,cmpf),scnt=0;
    fill(vis+1,vis+(M+N)*2+1,0);
    for(int i=1;i<=K;i++){
        addudge2(C[i].f,C[i].w),vis[C[i].f]=1;
        if(C[i%K+1].c!=C[i].c){
            S[++scnt]={C[i].c,C[i%K+1].c};
            sta[scnt]=aget(C[i].f).second;
        }
    }
    if(!scnt)return 0;
    for(int i=1;i<=(M+N)*2;i++){
        if(!vis[i])addudge2(i,0);
    }
    for(int i=1;i<=scnt;i+=2){
        dijkstra(sta[i]);
        for(int j=2;j<=scnt;j+=2){
            wcon[i][j]=wcon[j][i]=dis[sta[j]];
        }
    }
    for(int len=2;len<=scnt;len+=2){
        for(int i=1;i<=scnt;i++){
            dp[i][len]=dp[i%scnt+1][len-2]+wcon[i][(i+len-2)%scnt+1];
            for(int j=2;j<len;j+=2)minner(dp[i][len],dp[i][j]+dp[(i+j-1)%scnt+1][len-j]);
        }
    }
    lolo res=Inf;
    for(int i=1;i<=scnt;i++)minner(res,dp[i][scnt]);
    return res;
}
int main(){
    readis(N,M,T);V=(N+1)*(M+1);
    for(int i=1;i<N;i++){
        for(int j=1;j<=M;j++){
            cu=trans(i,j-1),cv=trans(i,j);
            readi(X),addudge1(cu,cv,X);
        }
    }
    for(int i=1;i<=N;i++){
        for(int j=1;j<M;j++){
            cu=trans(i-1,j),cv=trans(i,j);
            readi(X),addudge1(cu,cv,X);
        }
    }
    while(T--){
        readi(K);
        for(int i=1;i<=K;i++){
            readis(X,Y,cc);
            C[i]={X,Y,cc};
        }
        writil(solve());
        for(int i=1;i<=(M+N)*2;i+=2){
            auto [u,v]=aget(i);
            Gr2[u].clear(),Gr2[v].clear();
        }
    }
    return 0;
}
posted @ 2025-09-15 19:18  矞龙OrinLoong  阅读(22)  评论(0)    收藏  举报